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 baf1e6a2f12..7d04d4adec2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea/ +.vscode/ node_modules/ build .DS_Store @@ -5,5 +7,8 @@ build my-app* template/src/__tests__/__snapshots__/ lerna-debug.log -npm-debug.log packages/react-scripts/template/artifacts/coverage/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/.changelog diff --git a/.travis.yml b/.travis.yml index bb9c8112081..fa2c7d8b364 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,29 @@ --- language: node_js node_js: - - 0.10 - 4 - 6 + - 7 cache: directories: - node_modules - packages/create-react-app/node_modules - packages/react-scripts/node_modules -script: tasks/e2e.sh +install: true +script: + - 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi' + - 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi' + - 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi' env: - - USE_YARN=no - - USE_YARN=yes + global: + - USE_YARN=no + matrix: + - TEST_SUITE=simple + - TEST_SUITE=installs + - TEST_SUITE=kitchensink +matrix: + include: + - node_js: 0.10 + env: TEST_SUITE=simple + - node_js: 6 + env: USE_YARN=yes TEST_SUITE=simple diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8cdb4fecb..b96fd76db7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,63 +1,590 @@ +## 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)) + + Fixes an issue where running `create-react-app` failed on systems with Apache Hadoop installed because it falsely detected Hadoop YARN executable as Yarn package manager. + +#### Committers: 1 +- Ville Immonen ([fson](https://github.com/fson)) + +### Migrating from 0.8.4 to 0.8.5 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.5 +``` + +You may also optionally update the global command-line utility: + +``` +npm install -g create-react-app@1.0.3 +``` + ## 0.8.4 (December 11, 2016) #### :bug: Bug Fix * `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 @@ -107,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 @@ -226,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)) @@ -240,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)) @@ -394,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)) @@ -414,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)) @@ -572,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`) @@ -714,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 5110742f6ee..74ce845b1a5 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 --scripts-version @trunkclub/build 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. +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) @@ -116,14 +128,16 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast - [Adding Flow](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-flow) - [Adding Custom Environment Variables](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables) - [Can I Use Decorators?](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#can-i-use-decorators) -- [Integrating with a Node Backend](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#integrating-with-a-node-backend) +- [Integrating with an API Backend](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#integrating-with-an-api-backend) - [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. @@ -144,7 +158,7 @@ Please refer to the [User Guide](https://github.com/facebookincubator/create-rea **If you’re getting started** with React, use `create-react-app` to automate the build of your app. There is no configuration file, and `react-scripts` is the only extra build dependency in your `package.json`. Your environment will have everything you need to build a modern React app: -* React, JSX, and ES6 support. +* React, JSX, ES6, and Flow syntax support. * Language extras beyond ES6 like the object spread operator. * A dev server that lints for common errors. * Import CSS and image files directly from JavaScript. @@ -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: @@ -205,11 +224,11 @@ We are grateful to the authors of existing related projects for their ideas and ## Alternatives -If you don’t agree with the choices made in this project, you might want to explore alternatives with different tradeoffs. +If you don’t agree with the choices made in this project, you might want to explore alternatives with different tradeoffs.<br> 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 6645ea27971..46bc55b0245 100644 --- a/package.json +++ b/package.json @@ -4,21 +4,15 @@ "build": "node packages/react-scripts/scripts/build.js", "changelog": "lerna-changelog", "create-react-app": "tasks/cra.sh", - "e2e": "tasks/e2e.sh", + "e2e": "tasks/e2e-simple.sh", "postinstall": "lerna bootstrap", "publish": "tasks/release.sh", "start": "node packages/react-scripts/scripts/start.js", "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 index 7c9394ea371..f2f17142149 100755 --- 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( @@ -52,16 +53,20 @@ if (currentNodeVersion.split('.')[0] < 4) { process.exit(1); } -var fs = require('fs'); +var commander = require('commander'); +var fs = require('fs-extra'); var path = require('path'); var execSync = require('child_process').execSync; var spawn = require('cross-spawn'); var semver = require('semver'); -var pathExists = require('path-exists'); +var dns = require('dns'); +var tmp = require('tmp'); +var unpack = require('tar-pack').unpack; +var hyperquest = require('hyperquest'); var projectName; -var program = require('commander') +var program = commander .version(require('./package.json').version) .arguments('<project-directory>') .usage(chalk.green('<project-directory>') + ' [options]') @@ -70,6 +75,7 @@ var program = require('commander') }) .option('--verbose', 'print additional logs') .option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts') + .allowUnknownOption() .on('--help', function () { console.log(' Only ' + chalk.green('<project-directory>') + ' is required.'); console.log(); @@ -83,7 +89,7 @@ var program = require('commander') console.log(' ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new')); console.log(); }) - .parse(process.argv) + .parse(process.argv); if (typeof projectName === 'undefined') { console.error('Please specify the project directory:'); @@ -96,17 +102,28 @@ if (typeof projectName === 'undefined') { process.exit(1); } -createApp(projectName, program.verbose, program.scriptsVersion); +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') + .parse(process.argv) + +createApp(projectName, program.verbose, program.scriptsVersion, hiddenProgram.internalTestingTemplate); -function createApp(name, verbose, version) { +function createApp(name, verbose, version, template) { var root = path.resolve(name); var appName = path.basename(root); checkAppName(appName); - - if (!pathExists.sync(name)) { - fs.mkdirSync(root); - } else if (!isSafeToCreateProjectIn(root)) { + fs.ensureDirSync(name); + if (!isSafeToCreateProjectIn(root)) { console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.'); console.log('Try using a new directory name.'); process.exit(1); @@ -120,7 +137,7 @@ function createApp(name, verbose, version) { var packageJson = { name: appName, version: '0.1.0', - private: true, + private: true }; fs.writeFileSync( path.join(root, 'package.json'), @@ -129,65 +146,145 @@ function createApp(name, verbose, version) { 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); + run(root, appName, version, verbose, originalDirectory, template); } function shouldUseYarn() { try { - execSync('yarn --version', {stdio: 'ignore'}); + execSync('yarnpkg --version', {stdio: 'ignore'}); return true; } catch (e) { return false; } } -function install(packageToInstall, verbose, callback) { - var command; - var args; - if (shouldUseYarn()) { - command = 'yarn'; - 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) { +function run(root, appName, version, verbose, originalDirectory, template) { var packageToInstall = getInstallPackage(version); - var packageName = getPackageName(packageToInstall); + var allDependencies = ['react', 'react-dom', packageToInstall]; - install(packageToInstall, verbose, function(code, command, args) { - if (code !== 0) { - console.error(chalk.cyan(command + ' ' + args.join(' ')) + ' failed'); - process.exit(1); - } - - 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); - }); + 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) { @@ -202,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 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; } - return installPackage; + 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) { @@ -242,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( @@ -264,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 1c466c7f876..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.2", + "version": "1.3.0", "keywords": [ "react" ], @@ -23,7 +23,11 @@ "chalk": "^1.1.1", "commander": "^2.9.0", "cross-spawn": "^4.0.0", - "path-exists": "^2.1.0", - "semver": "^5.0.3" + "fs-extra": "^1.0.0", + "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 3c6a10f62e0..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. @@ -117,6 +140,7 @@ Extracts and prettifies warning and error messages from webpack [stats](https:// ```js var webpack = require('webpack'); var config = require('../config/webpack.config.dev'); +var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); var compiler = webpack(config); @@ -132,12 +156,12 @@ compiler.plugin('done', function(stats) { } if (messages.errors.length) { console.log('Failed to compile.'); - messages.errors.forEach(console.log); + messages.errors.forEach(e => console.log(e)); return; } if (messages.warnings.length) { console.log('Compiled with warnings.'); - messages.warnings.forEach(console.log); + messages.warnings.forEach(w => console.log(w)); } }); ``` @@ -160,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. @@ -178,12 +202,13 @@ 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 var prompt = require('react-dev-utils/prompt'); + prompt( 'Are you sure you want to eat all the candy?', /* isYesDefault */ false 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 2f0ca0a74cc..ad99d9065b5 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@trunkclub/react-dev-utils", - "version": "1.1.1", + "version": "2.0.0-alpha.0", "description": "Webpack utilities used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,24 +11,28 @@ "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", - "sockjs-client": "1.0.3", + "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 3ac03d58464..a3660dc05b5 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 @@ -248,6 +250,7 @@ connection.onmessage = function(e) { case 'hash': handleAvailableHash(message.data); break; + case 'still-ok': case 'ok': handleSuccess(); break; @@ -318,4 +321,4 @@ function tryApplyUpdates(onHotUpdateSuccess) { } ); } -}; +} diff --git a/packages/react-scripts/.npmignore b/packages/react-scripts/.npmignore new file mode 100644 index 00000000000..76163ab966a --- /dev/null +++ b/packages/react-scripts/.npmignore @@ -0,0 +1 @@ +/fixtures 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 0ad234e9475..51e66c57308 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 checkNodeVersion = require('../utils/checkNodeVersion'); var script = process.argv[2]; diff --git a/packages/react-scripts/config/env.js b/packages/react-scripts/config/env.js index b2dace243ca..7614ce08495 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 TC_* environment variables and prepare them to be // injected into the application via DefinePlugin in Webpack configuration. @@ -15,25 +16,33 @@ var REACT_APP = /^(TC_|PORT)/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 7fd7efa47bb..1eed33546d7 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 5d67c32168d..64cf62d1028 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'), @@ -53,17 +86,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'), @@ -75,25 +110,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 (!process.env.PROJECT_DIR && __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 new file mode 100644 index 00000000000..14031a1688d --- /dev/null +++ b/packages/react-scripts/config/polyfills.js @@ -0,0 +1,26 @@ +// @remove-on-eject-begin +/** + * 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. + */ +// @remove-on-eject-end +'use strict'; + +if (typeof Promise === 'undefined') { + // Rejection tracking prevents a common issue where React gets into an + // inconsistent state due to an error, but it gets swallowed by a Promise, + // and the user has no idea what causes React's erratic future behavior. + require('promise/lib/rejection-tracking').enable(); + window.Promise = require('promise/lib/es6-extensions.js'); +} + +// fetch() polyfill for making API calls. +require('whatwg-fetch'); + +// Object.assign() is commonly used with React. +// It will use the native implementation if it's present and isn't buggy. +Object.assign = require('object-assign'); diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index ba3ba785d85..9c6c55b580f 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'); @@ -70,13 +71,15 @@ module.exports = { publicPath: publicPath }, resolve: { + root: paths.appSrc, // This allows you to set a fallback for where Webpack should look for modules. // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. // 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: [].concat(paths.appNodeModules, paths.nodePaths), - root: paths.appSrc, + // 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: @@ -91,14 +94,15 @@ 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, - fallback: paths.appNodeModules, + // @remove-on-eject-end + // Fallback to any hoisted modules when dealing with linked libraries + fallback: paths.appNodeModules }, - // @remove-on-eject-end module: { noParse: [/\.elm$/], preLoaders: [ @@ -114,30 +118,17 @@ module.exports = { loader: 'eslint-loader', test: /\.(jsx?|es6)$/, include: paths.appSrc, - query: { - configFile: path.join(__dirname, '../.eslintrc'), - // All warnings and errors are passed to webpack as warnings to allow - // webpack compilation to continue. - emitWarning: true, - useEslintrc: false - } } ], 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$/, @@ -212,8 +203,18 @@ 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, + emitWarning: true, + }, + // @remove-on-eject-end // We use PostCSS for autoprefixing only. postcss: function() { return [ @@ -231,13 +232,11 @@ module.exports = { new ProgressBarPlugin({ summary: false }), - // 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(Object.keys(env['process.env']).reduce(function (e, key) { - e[key] = JSON.parse(env['process.env'][key]) - return e - }, {})), + new InterpolateHtmlPlugin(env.raw), // Generates an `index.html` file with the <script> injected. new HtmlWebpackPlugin({ inject: true, @@ -251,7 +250,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 9e42dc27407..15652e0261c 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'); @@ -27,31 +28,16 @@ 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); // Create an instance of the text extract plugin to be used by both @@ -60,10 +46,22 @@ const extractCSS = new ExtractTextPlugin('static/css/[name].[contenthash:8].css' // 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. @@ -90,13 +88,15 @@ module.exports = { publicPath: publicPath }, resolve: { + root: paths.appSrc, // This allows you to set a fallback for where Webpack should look for modules. // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. // 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: [].concat(paths.appNodeModules, paths.nodePaths), - root: paths.appSrc, + // 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: @@ -108,14 +108,15 @@ 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, + // @remove-on-eject-end + // Fallback to any hoisted modules when dealing with linked libraries fallback: paths.appNodeModules, }, - // @remove-on-eject-end module: { noParse: [/\.elm$/], // First, run the linter. @@ -135,18 +136,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$/, @@ -232,6 +229,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 @@ -239,8 +238,8 @@ 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'), - useEslintrc: false + configFile: path.join(__dirname, '../eslintrc'), + useEslintrc: false, }, // @remove-on-eject-end // We use PostCSS for autoprefixing only. @@ -257,14 +256,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(Object.keys(env['process.env']).reduce(function (e, key) { - e[key] = JSON.parse(env['process.env'][key]) - return e - }, {})), + new InterpolateHtmlPlugin(env.raw), // Generates an `index.html` file with the <script> injected. new HtmlWebpackPlugin({ inject: true, @@ -292,7 +289,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), // https://github.com/lodash/lodash-webpack-plugin new LodashModuleReplacementPlugin, // This helps ensure the builds are consistent if source hasn't changed: 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 new file mode 100644 index 00000000000..c14b2828d16 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-app"] +} diff --git a/packages/react-scripts/fixtures/kitchensink/.env b/packages/react-scripts/fixtures/kitchensink/.env new file mode 100644 index 00000000000..3e6e8a5a360 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/.env @@ -0,0 +1 @@ +REACT_APP_FILE_ENV_MESSAGE=fromtheenvfile diff --git a/packages/react-scripts/fixtures/kitchensink/.flowconfig b/packages/react-scripts/fixtures/kitchensink/.flowconfig new file mode 100644 index 00000000000..c658362285f --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/.flowconfig @@ -0,0 +1,8 @@ +[ignore] +<PROJECT_ROOT>/node_modules/fbjs/.* + +[include] + +[libs] + +[options] diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json new file mode 100644 index 00000000000..8a54954cf0f --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + "babel-register": "6.22.0", + "babel-polyfill": "6.20.0", + "chai": "3.5.0", + "jsdom": "9.8.3", + "mocha": "3.2.0", + "test-integrity": "1.0.0" + } +} diff --git a/packages/react-scripts/fixtures/kitchensink/gitignore b/packages/react-scripts/fixtures/kitchensink/gitignore new file mode 100644 index 00000000000..6c96c5cff12 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/gitignore @@ -0,0 +1,15 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules + +# testing +coverage + +# production +build + +# misc +.DS_Store +.env +npm-debug.log diff --git a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js new file mode 100644 index 00000000000..c3169c987a9 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js @@ -0,0 +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('PUBLIC_URL', async () => { + const doc = await initDOM('public-url') + + 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('shell env variables', async () => { + const doc = await initDOM('shell-env-variables') + + 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 new file mode 100644 index 00000000000..3cfb182148a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js @@ -0,0 +1,65 @@ +/** + * 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') +const path = require('path') +const { expect } = require('chai') + +let getMarkup +let resourceLoader + +if (process.env.E2E_FILE) { + const file = path.isAbsolute(process.env.E2E_FILE) + ? process.env.E2E_FILE + : path.join(process.cwd(), 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.replace(pathPrefix, '')), 'utf8') + ) +} else if (process.env.E2E_URL) { + getMarkup = () => new Promise(resolve => { + http.get(process.env.E2E_URL, (res) => { + let rawData = '' + res.on('data', chunk => rawData += chunk) + res.on('end', () => resolve(rawData)) + }) + }) + + resourceLoader = (resource, callback) => resource.defaultFetch(callback) +} else { + it.only('can run jsdom (at least one of "E2E_FILE" or "E2E_URL" environment variables must be provided)', () => { + expect(new Error('This isn\'t the error you are looking for.')).to.be.undefined() + }) +} + +export default feature => new Promise(async resolve => { + const markup = await getMarkup() + const host = process.env.E2E_URL || 'http://www.example.org/spa:3000' + const doc = jsdom.jsdom(markup, { + features: { + FetchExternalResources: ['script', 'css'], + ProcessExternalResources: ['script'], + }, + created: (_, win) => win.addEventListener('ReactFeatureDidMount', () => resolve(doc), true), + deferClose: true, + resourceLoader, + url: `${host}#${feature}`, + virtualConsole: jsdom.createVirtualConsole().sendTo(console), + }) + + doc.close() +}) diff --git a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js new file mode 100644 index 00000000000..8fe1990f20a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js @@ -0,0 +1,105 @@ +/** + * 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('Language syntax', () => { + it('array destructuring', async () => { + const doc = await initDOM('array-destructuring') + + expect(doc.getElementById('feature-array-destructuring').childElementCount).to.equal(4) + }) + + it('array spread', async () => { + const doc = await initDOM('array-spread') + + expect(doc.getElementById('feature-array-spread').childElementCount).to.equal(4) + }) + + it('async/await', async () => { + const doc = await initDOM('async-await') + + expect(doc.getElementById('feature-async-await').childElementCount).to.equal(4) + }) + + it('class properties', async () => { + const doc = await initDOM('class-properties') + + expect(doc.getElementById('feature-class-properties').childElementCount).to.equal(4) + }) + + it('computed properties', async () => { + const doc = await initDOM('computed-properties') + + expect(doc.getElementById('feature-computed-properties').childElementCount).to.equal(4) + }) + + it('custom interpolation', async () => { + const doc = await initDOM('custom-interpolation') + + expect(doc.getElementById('feature-custom-interpolation').childElementCount).to.equal(4) + }) + + it('default parameters', async () => { + const doc = await initDOM('default-parameters') + + expect(doc.getElementById('feature-default-parameters').childElementCount).to.equal(4) + }) + + it('destructuring and await', async () => { + const doc = await initDOM('destructuring-and-await') + + expect(doc.getElementById('feature-destructuring-and-await').childElementCount).to.equal(4) + }) + + it('generators', async () => { + const doc = await initDOM('generators') + + expect(doc.getElementById('feature-generators').childElementCount).to.equal(4) + }) + + it('object destructuring', async () => { + const doc = await initDOM('object-destructuring') + + expect(doc.getElementById('feature-object-destructuring').childElementCount).to.equal(4) + }) + + it('object spread', async () => { + const doc = await initDOM('object-spread') + + expect(doc.getElementById('feature-object-spread').childElementCount).to.equal(4) + }) + + it('promises', async () => { + const doc = await initDOM('promises') + + expect(doc.getElementById('feature-promises').childElementCount).to.equal(4) + }) + + it('rest + default', async () => { + const doc = await initDOM('rest-and-default') + + expect(doc.getElementById('feature-rest-and-default').childElementCount).to.equal(4) + }) + + it('rest parameters', async () => { + const doc = await initDOM('rest-parameters') + + expect(doc.getElementById('feature-rest-parameters').childElementCount).to.equal(4) + }) + + it('template interpolation', async () => { + const doc = await initDOM('template-interpolation') + + expect(doc.getElementById('feature-template-interpolation').childElementCount).to.equal(4) + }) + }) +}) diff --git a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js new file mode 100644 index 00000000000..8cf4312c210 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js @@ -0,0 +1,59 @@ +/** + * 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('Webpack plugins', () => { + it('css inclusion', async () => { + const doc = await initDOM('css-inclusion') + + expect(doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, '')) + .to.match(/#feature-css-inclusion\{background:.+;color:.+}/) + }) + + it('image inclusion', async () => { + const doc = await initDOM('image-inclusion') + + expect(doc.getElementById('feature-image-inclusion').src).to.match(/^data:image\/jpeg;base64.+==$/) + }) + + it('no ext inclusion', async () => { + const doc = await initDOM('no-ext-inclusion') + + expect(doc.getElementById('feature-no-ext-inclusion').textContent) + .to.equal('This is just a file without an extension.') + }) + + it('json inclusion', async () => { + const doc = await initDOM('json-inclusion') + + 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') + + expect(doc.getElementById('feature-svg-inclusion').src).to.match(/\/static\/media\/logo\..+\.svg$/) + }) + + it('unknown ext inclusion', async () => { + const doc = await initDOM('unknown-ext-inclusion') + + expect(doc.getElementById('feature-unknown-ext-inclusion').textContent).to.equal('Whoooo, spooky!.') + }) + }) +}) diff --git a/packages/react-scripts/fixtures/kitchensink/public/favicon.ico b/packages/react-scripts/fixtures/kitchensink/public/favicon.ico new file mode 100644 index 00000000000..5c125de5d89 Binary files /dev/null and b/packages/react-scripts/fixtures/kitchensink/public/favicon.ico differ diff --git a/packages/react-scripts/fixtures/kitchensink/public/index.html b/packages/react-scripts/fixtures/kitchensink/public/index.html new file mode 100644 index 00000000000..ce76aae90a4 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/public/index.html @@ -0,0 +1,12 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> + <title>React App + + +
+ + diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js new file mode 100644 index 00000000000..dd1b1388c5a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/App.js @@ -0,0 +1,153 @@ +/** + * 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, createElement } from 'react'; + +class BuiltEmitter extends Component { + static propTypes = { + feature: PropTypes.func.isRequired + } + + componentDidMount() { + 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(); + } + } + + handleReady() { + document.dispatchEvent(new Event('ReactFeatureDidMount')); + } + + render() { + const { + props: { feature }, + handleReady + } = this; + return ( +
+ {createElement(feature, { + onReady: handleReady + })} +
+ ); + } +} + +class App extends Component { + constructor(props) { + super(props); + + this.state = { feature: null }; + + this.setFeature = this.setFeature.bind(this); + } + + componentDidMount() { + const feature = location.hash.slice(1); + switch (feature) { + case 'array-destructuring': + require.ensure([], () => this.setFeature(require('./features/syntax/ArrayDestructuring').default)); + break; + case 'array-spread': + require.ensure([], () => this.setFeature(require('./features/syntax/ArraySpread').default)); + break; + case 'async-await': + require.ensure([], () => this.setFeature(require('./features/syntax/AsyncAwait').default)); + break; + case 'class-properties': + require.ensure([], () => this.setFeature(require('./features/syntax/ClassProperties').default)); + break; + case 'computed-properties': + require.ensure([], () => this.setFeature(require('./features/syntax/ComputedProperties').default)); + break; + case 'css-inclusion': + require.ensure([], () => this.setFeature(require('./features/webpack/CssInclusion').default)); + break; + case 'custom-interpolation': + require.ensure([], () => this.setFeature(require('./features/syntax/CustomInterpolation').default)); + break; + case 'default-parameters': + require.ensure([], () => this.setFeature(require('./features/syntax/DefaultParameters').default)); + break; + case 'destructuring-and-await': + require.ensure([], () => this.setFeature(require('./features/syntax/DestructuringAndAwait').default)); + break; + case 'file-env-variables': + require.ensure([], () => this.setFeature(require('./features/env/FileEnvVariables').default)); + break; + case 'generators': + require.ensure([], () => this.setFeature(require('./features/syntax/Generators').default)); + break; + case 'image-inclusion': + require.ensure([], () => this.setFeature(require('./features/webpack/ImageInclusion').default)); + break; + 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; + case 'no-ext-inclusion': + require.ensure([], () => this.setFeature(require('./features/webpack/NoExtInclusion').default)); + break; + case 'object-destructuring': + require.ensure([], () => this.setFeature(require('./features/syntax/ObjectDestructuring').default)); + break; + case 'object-spread': + require.ensure([], () => this.setFeature(require('./features/syntax/ObjectSpread').default)); + break; + 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; + case 'rest-parameters': + require.ensure([], () => this.setFeature(require('./features/syntax/RestParameters').default)); + break; + case 'shell-env-variables': + require.ensure([], () => this.setFeature(require('./features/env/ShellEnvVariables').default)); + break; + case 'svg-inclusion': + require.ensure([], () => this.setFeature(require('./features/webpack/SvgInclusion').default)); + break; + case 'template-interpolation': + require.ensure([], () => this.setFeature(require('./features/syntax/TemplateInterpolation').default)); + break; + case 'unknown-ext-inclusion': + require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default)); + break; + default: throw new Error(`Missing feature "${feature}"`); + } + } + + setFeature(feature) { + this.setState({ feature }); + } + + render() { + const { feature } = this.state; + if (feature !== null) { + return ; + } + return null; + } +} + +export default App; diff --git a/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js new file mode 100644 index 00000000000..a1fe21e48ff --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js @@ -0,0 +1,15 @@ +/** + * 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' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } +] diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js new file mode 100644 index 00000000000..ff874b62812 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.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 () => ( + {process.env.REACT_APP_FILE_ENV_MESSAGE}. +) 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 new file mode 100644 index 00000000000..5a0fed4c649 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.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 FileEnvVariables from './FileEnvVariables'; + +describe('.env variables', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js new file mode 100644 index 00000000000..e4b868e47b6 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js @@ -0,0 +1,41 @@ +/** + * 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 Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..7e076dade75 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('NODE_PATH', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 () => ( + {process.env.PUBLIC_URL}. +) 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(, 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 new file mode 100644 index 00000000000..243bfc00935 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.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 () => ( + {process.env.REACT_APP_SHELL_ENV_MESSAGE}. +) 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 new file mode 100644 index 00000000000..13707697591 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.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 ShellEnvVariables from './ShellEnvVariables'; + +describe('shell env variables', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js new file mode 100644 index 00000000000..48516d43fd3 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js @@ -0,0 +1,50 @@ +/** + * 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 [ + [1, '1'], + [2, '2'], + [3, '3'], + [4, '4'] + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => { + const [id, name] = user; + return
{name}
+ })} +
+ ); + } +} 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 new file mode 100644 index 00000000000..42dde0f1c35 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('array destructuring', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..9998589a138 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + ...users + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load([{ id: 42, name: '42' }]); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..9ed7633ea35 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('array spread', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..b5b6013f93c --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = await load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..02713981799 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('async/await', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..63bd7ab0512 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js @@ -0,0 +1,37 @@ +/** + * 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 + } + + users = [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ]; + + componentDidMount() { + this.props.onReady() + } + + render() { + return ( +
+ {this.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..5b038e76d90 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('class properties', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..e345283eaaa --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: 1, [prefix + 'name']: '1' }, + { id: 2, [prefix + 'name']: '2' }, + { id: 3, [prefix + 'name']: '3' }, + { id: 4, [prefix + 'name']: '4' } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load('user_'); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.user_name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..91b697fe0a0 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('computed properties', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..399280d0190 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js @@ -0,0 +1,59 @@ +/** + * 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*/) + .map(rule => rule.split(/\s*:\s*/)) + .reduce((rules, rule) => ({ ...rules, [rule[0]]: rule[1] }), {}); + +function load() { + return [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + const veryInlineStyle = styled` + background: palevioletred; + color: papayawhip; + `; + + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..ac468db76a8 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('custom interpolation', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..fd4054e56cb --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: id + 1, name: '1' }, + { id: id + 2, name: '2' }, + { id: id + 3, name: '3' }, + { id: id + 4, name: '4' } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..26d5184eb11 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('default parameters', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..c2345f5ce54 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js @@ -0,0 +1,49 @@ +/** + * 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: [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ] }; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const { users } = await load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..c7d74d8012b --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('destructuring and await', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..170f00a7f0d --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js @@ -0,0 +1,51 @@ +/** + * 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; + while (i <= limit) { + yield { id: i, name: i }; + i++; + } +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + componentDidMount() { + const users = []; + for (let user of load(4)) { + users.push(user); + } + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..3bbe2d4f703 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('generators', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..025920b7f31 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js @@ -0,0 +1,50 @@ +/** + * 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 [ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => { + const { id, name } = user; + return
{name}
+ })} +
+ ); + } +} 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 new file mode 100644 index 00000000000..503b2cb11b6 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('object destructuring', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..d5de0aef31a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: 1, name: '1', ...baseUser }, + { id: 2, name: '2', ...baseUser }, + { id: 3, name: '3', ...baseUser }, + { id: 4, name: '4', ...baseUser } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load({ age: 42 }); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}: {user.age}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..09a92ce05a2 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('object spread', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..8b362fccf2c --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js @@ -0,0 +1,50 @@ +/** + * 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([ + { id: 1, name: '1' }, + { id: 2, name: '2' }, + { id: 3, name: '3' }, + { id: 4, name: '4' } + ]); +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + componentDidMount() { + load().then(users => { + this.setState({ users }); + }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..6f27e70d3e7 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('promises', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..47d011f2eae --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: id + 1, name: '1' }, + { id: id + 2, name: '2' }, + { id: id + 3, name: '3' }, + rest.user + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load(); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..e48b7dc59b9 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('rest + default', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..1a6cf7c1687 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: id + 1, name: '1' }, + { id: id + 2, name: '2' }, + { id: id + 3, name: '3' }, + rest.user + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load({ id: 0, user: { id: 42, name: '42' } }); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..c1eb90e0dbd --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('rest parameters', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..ff46e1c72f8 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js @@ -0,0 +1,49 @@ +/** + * 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 [ + { id: 1, name: `${name}1` }, + { id: 2, name: `${name}2` }, + { id: 3, name: `${name}3` }, + { id: 4, name: `${name}4` } + ]; +} + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + + constructor(props) { + super(props); + this.state = { users: [] }; + } + + async componentDidMount() { + const users = load('user_'); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); + } + + render() { + return ( +
+ {this.state.users.map(user => ( +
{user.name}
+ ))} +
+ ); + } +} 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 new file mode 100644 index 00000000000..ff32baaedd0 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js @@ -0,0 +1,21 @@ +/** + * 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'; + +describe('template interpolation', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + return new Promise(resolve => { + ReactDOM.render(, 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 new file mode 100644 index 00000000000..7c46c7208b7 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js @@ -0,0 +1,15 @@ +/** + * 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' + +export default () => ( +

We love useless text.

+) 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 new file mode 100644 index 00000000000..e315ee33b38 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.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 CssInclusion from './CssInclusion'; + +describe('css inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js new file mode 100644 index 00000000000..c4560ff9dae --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js @@ -0,0 +1,15 @@ +/** + * 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' + +export default () => ( + tiniest cat +) 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 new file mode 100644 index 00000000000..042d2da3088 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.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 ImageInclusion from './ImageInclusion'; + +describe('image inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js new file mode 100644 index 00000000000..552a8be7b1a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js @@ -0,0 +1,15 @@ +/** + * 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' + +export default () => ( + {abstract} +) 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 new file mode 100644 index 00000000000..ee150f629a6 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.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 JsonInclusion from './JsonInclusion'; + +describe('JSON inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); 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

{v}

; +}; 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(, 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 new file mode 100644 index 00000000000..fb07359fdc9 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.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 aFileWithoutExt from './assets/aFileWithoutExt' + +const text = aFileWithoutExt.includes('base64') + ? atob(aFileWithoutExt.split('base64,')[1]).trim() + : aFileWithoutExt + +export default () => ( +

{text}.

+) 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 new file mode 100644 index 00000000000..a08b5d21b0c --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.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 NoExtInclusion from './NoExtInclusion'; + +describe('no ext inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js new file mode 100644 index 00000000000..f82e5f2911c --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js @@ -0,0 +1,15 @@ +/** + * 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' + +export default () => ( + logo +) 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 new file mode 100644 index 00000000000..585c6c19d8a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.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 SvgInclusion from './SvgInclusion'; + +describe('svg inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js new file mode 100644 index 00000000000..8734064c882 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.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 aFileWithExtUnknown from './assets/aFileWithExt.unknown' + +const text = aFileWithExtUnknown.includes('base64') + ? atob(aFileWithExtUnknown.split('base64,')[1]).trim() + : aFileWithExtUnknown + +export default () => ( +

{text}.

+) 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 new file mode 100644 index 00000000000..73a8016e17b --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.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 UnknownExtInclusion from './UnknownExtInclusion'; + +describe('unknown ext inclusion', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + }); +}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown new file mode 100644 index 00000000000..a5de8eb3264 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithExt.unknown @@ -0,0 +1 @@ +Whoooo, spooky! diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt new file mode 100644 index 00000000000..dbcc1afda65 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/aFileWithoutExt @@ -0,0 +1 @@ +This is just a file without an extension diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json new file mode 100644 index 00000000000..5d21a7168a8 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/abstract.json @@ -0,0 +1,3 @@ +{ + "abstract": "This is an abstract." +} diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg new file mode 100644 index 00000000000..6b60c1042f5 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css new file mode 100644 index 00000000000..c399d1acada --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/style.css @@ -0,0 +1,4 @@ +#feature-css-inclusion { + background: palevioletred; + color: papayawhip; +} diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg new file mode 100644 index 00000000000..c658e9922a7 Binary files /dev/null and b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/assets/tiniest-cat.jpg differ diff --git a/packages/react-scripts/fixtures/kitchensink/src/index.js b/packages/react-scripts/fixtures/kitchensink/src/index.js new file mode 100644 index 00000000000..dc1143691f2 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js new file mode 100644 index 00000000000..c844f831d31 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js @@ -0,0 +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 28ea19efd4b..84079d6ef8f 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -1,7 +1,7 @@ { "name": "@trunkclub/build", - "version": "6.1.1", - "upstream-version": "0.8.4", + "version": "7.0.0", + "upstream-version": "0.9.5", "description": "Configuration and scripts for Create React App. Fork maintained by Trunk Club", "repository": "trunkclub/create-react-app", "license": "BSD-3-Clause", @@ -16,8 +16,8 @@ "url": "https://github.com/trunkclub/create-react-app/issues" }, "files": [ - ".babelrc", - ".eslintrc", + "babelrc", + "eslintrc", "bin", "config", "scripts", @@ -30,16 +30,17 @@ }, "dependencies": { "@trunkclub/eslint-config": "3.2.0", - "@trunkclub/react-dev-utils": "1.1.0", - "autoprefixer": "6.5.1", + "@trunkclub/react-dev-utils": "2.0.0-alpha.0", + "autoprefixer": "6.7.2", "babel-cli": "6.18.0", - "babel-core": "6.17.0", + "babel-core": "6.22.1", "babel-eslint": "7.1.1", - "babel-jest": "17.0.2", - "babel-loader": "6.2.7", + "babel-jest": "18.0.0", + "babel-loader": "6.2.10", "babel-plugin-module-resolver": "2.3.0", "babel-polyfill": "6.16.0", "babel-preset-trunkclub": "4.0.0", + "babel-runtime": "^6.20.0", "case-sensitive-paths-webpack-plugin": "1.1.4", "chalk": "1.1.3", "cjsx-loader": "3.0.0", @@ -47,33 +48,32 @@ "coffee-script": "1.11.1", "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", "elm-webpack-loader": "3.0.6", - "eslint": "3.8.1", + "eslint": "3.16.1", "eslint-loader": "1.6.0", "eslint-plugin-flowtype-errors": "3.0.0", "eslint-plugin-react": "6.4.1", "extract-text-webpack-plugin": "1.0.1", - "file-loader": "0.9.0", + "file-loader": "0.10.0", "filesize": "3.3.0", "flow-bin": "0.42.0", "fs-extra": "1.0.0", "git-rev-sync": "1.8.0", "gzip-size": "3.0.0", "html-webpack-plugin": "2.24.0", - "http-proxy-middleware": "0.17.2", - "jest": "17.0.2", + "http-proxy-middleware": "0.17.3", + "jest": "18.1.0", "json-loader": "0.5.4", "lodash-webpack-plugin": "0.11.2", "node-sass": "3.11.1", - "object-assign": "4.1.0", + "object-assign": "4.1.1", "path-exists": "2.1.0", - "postcss-loader": "1.0.0", + "postcss-loader": "1.2.2", "progress-bar-webpack-plugin": "1.9.0", "promise": "7.1.1", - "react-dev-utils": "^0.4.2", "recursive-readdir": "2.1.0", "rimraf": "2.5.4", "sass-loader": "4.0.2", @@ -85,7 +85,7 @@ "webpack-bundle-analyzer": "1.4.1", "webpack-dev-server": "1.16.2", "webpack-manifest-plugin": "1.1.0", - "whatwg-fetch": "1.0.0" + "whatwg-fetch": "2.0.2" }, "devDependencies": { "babel-runtime": "^6.11.6", @@ -95,6 +95,6 @@ "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 e8525e706f7..2a767be4834 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'; // Load environment variables from .env file. Suppress warnings using silent @@ -22,105 +23,38 @@ process.env.NODE_ENV = 'production'; var chalk = require('chalk'); var fs = require('fs-extra'); var path = require('path'); -var pathExists = require('path-exists'); -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('@trunkclub/react-dev-utils/checkRequiredFiles'); var recursive = require('recursive-readdir'); var stripAnsi = require('strip-ansi'); +var FileSizeReporter = require('@trunkclub/react-dev-utils/FileSizeReporter'); +var measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild; +var printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; -var useYarn = pathExists.sync(paths.yarnLockFile); +var useYarn = fs.existsSync(paths.yarnLockFile); // Warn and crash if required files are missing 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)); @@ -132,7 +66,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) { @@ -146,7 +80,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); } @@ -155,20 +89,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(); @@ -183,7 +117,8 @@ function build(previousSizeMap) { console.log(' ' + chalk.dim('// ...')); console.log(' ' + chalk.yellow('"scripts"') + ': {'); console.log(' ' + chalk.dim('// ...')); - console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"npm run build&&gh-pages -d build"')); + console.log(' ' + chalk.yellow('"predeploy"') + ': ' + chalk.yellow('"npm run build",')); + console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"gh-pages -d build"')); console.log(' }'); console.log(); console.log('Then run:'); @@ -199,30 +134,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 e5a64118556..f90136af34c 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'); @@ -29,8 +30,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))) { @@ -89,13 +90,19 @@ 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; - console.log(' Removing ' + cyan(ownPackageName) + ' from devDependencies'); - delete appPackage.devDependencies[ownPackageName]; + if (appPackage.devDependencies[ownPackageName]) { + console.log(' Removing ' + cyan(ownPackageName) + ' from devDependencies'); + delete appPackage.devDependencies[ownPackageName]; + } + if (appPackage.dependencies[ownPackageName]) { + console.log(' Removing ' + cyan(ownPackageName) + ' from dependencies'); + delete appPackage.dependencies[ownPackageName]; + } Object.keys(ownPackage.dependencies).forEach(function (key) { // For some reason optionalDependencies end up in dependencies after install @@ -109,14 +116,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(); @@ -124,13 +134,12 @@ prompt( // Add Jest config console.log(' Adding ' + cyan('Jest') + ' configuration'); appPackage.jest = createJestConfig( - filePath => path.join('', filePath), + filePath => path.posix.join('', filePath), null, true ); // Add Babel config - console.log(' Adding ' + cyan('Babel') + ' preset'); appPackage.babel = babelConfig; @@ -140,17 +149,28 @@ prompt( fs.writeFileSync( path.join(appPath, 'package.json'), - JSON.stringify(appPackage, null, 2) + JSON.stringify(appPackage, null, 2) + '\n' ); console.log(); - if (pathExists.sync(paths.yarnLockFile)) { + // "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('yarn', [], {stdio: 'inherit'}); + 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 ab9069f574f..abf3b63e704 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -7,17 +7,18 @@ * 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'); -var pathExists = require('path-exists'); var chalk = require('chalk'); -module.exports = function(appPath, appName, verbose, originalDirectory) { +module.exports = function(appPath, appName, verbose, originalDirectory, template) { var ownPackageName = require(path.join(__dirname, '..', 'package.json')).name; var ownPath = path.join(appPath, 'node_modules', ownPackageName); var appPackage = require(path.join(appPath, 'package.json')); - var useYarn = pathExists.sync(path.join(appPath, 'yarn.lock')); + var useYarn = fs.existsSync(path.join(appPath, 'yarn.lock')); // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {}; @@ -38,13 +39,19 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { JSON.stringify(appPackage, null, 2) ); - var readmeExists = pathExists.sync(path.join(appPath, 'README.md')); + var readmeExists = fs.existsSync(path.join(appPath, 'README.md')); if (readmeExists) { fs.renameSync(path.join(appPath, 'README.md'), path.join(appPath, 'README.old.md')); } // Copy the files for the user - fs.copySync(path.join(ownPath, 'template'), appPath); + var templatePath = template ? path.resolve(originalDirectory, template) : path.join(ownPath, 'template'); + if (fs.existsSync(templatePath)) { + fs.copySync(templatePath, appPath); + } else { + console.error('Could not locate supplied template: ' + chalk.green(templatePath)); + return; + } // Rename gitignore after the fact to prevent npm from renaming it to .npmignore // See: https://github.com/npm/npm/issues/1862 @@ -61,13 +68,11 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { } }); - // 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; if (useYarn) { - command = 'yarn'; + command = 'yarnpkg'; args = ['add']; } else { command = 'npm'; @@ -79,53 +84,78 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { } args.push('react', 'react-dom', 'babel-runtime'); - console.log('Installing react and react-dom using ' + command + '...'); - console.log(); + // Install additional template dependencies, if present + var templateDependenciesPath = path.join(appPath, '.template.dependencies.json'); + if (fs.existsSync(templateDependenciesPath)) { + var templateDependencies = require(templateDependenciesPath).dependencies; + args = args.concat(Object.keys(templateDependencies).map(function (key) { + return key + '@' + templateDependencies[key]; + })); + fs.unlinkSync(templateDependenciesPath); + } - var proc = spawn(command, args, {stdio: 'inherit'}); - proc.on('close', function (code) { - if (code !== 0) { + // 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.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 fae6f7d524a..6b86111b8a7 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 @@ -23,17 +25,18 @@ var WebpackDevServer = require('webpack-dev-server'); var historyApiFallback = require('connect-history-api-fallback'); var httpProxyMiddleware = require('http-proxy-middleware'); var detect = require('detect-port'); -var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +var checkRequiredFiles = require('@trunkclub/react-dev-utils/checkRequiredFiles'); var clearConsole = require('@trunkclub/react-dev-utils/clearConsole'); var formatWebpackMessages = require('@trunkclub/react-dev-utils/formatWebpackMessages'); var getProcessForPort = require('@trunkclub/react-dev-utils/getProcessForPort'); var pathExists = require('path-exists'); var prompt = require('@trunkclub/react-dev-utils/prompt'); +var fs = require('fs'); var config = require('../config/webpack.config.dev'); var paths = require('../config/paths'); var formatTime = require('../utils/formatTime'); -var useYarn = pathExists.sync(paths.yarnLockFile); +var useYarn = fs.existsSync(paths.yarnLockFile); var cli = useYarn ? 'yarn' : 'npm'; var isInteractive = process.stdout.isTTY; @@ -43,7 +46,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; var time = +new Date(); @@ -174,6 +177,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. @@ -189,7 +200,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. @@ -200,7 +211,8 @@ function addMiddleware(devServer) { onError: onProxyError(proxy), secure: false, changeOrigin: true, - ws: true + ws: true, + xfwd: true }); devServer.use(mayProxy, hpm); @@ -229,7 +241,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%: // // 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 @@ -263,7 +275,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); } @@ -278,7 +290,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 c7731508e5a..2f3317fd76d 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 = ''; @@ -26,18 +28,6 @@ if (!process.env.CI && argv.indexOf('--coverage') < 0) { argv.push('--watch'); } -// A temporary hack to clear terminal correctly. -// You can remove this after updating to Jest 18 when it's out. -// https://github.com/facebook/jest/pull/2230 -var realWrite = process.stdout.write; -var CLEAR = process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; -process.stdout.write = function(chunk, encoding, callback) { - if (chunk === '\x1B[2J\x1B[H') { - chunk = CLEAR; - } - return realWrite.call(this, chunk, encoding, callback); -}; - // @remove-on-eject-begin // This is not necessary after eject because we embed config into package.json. const createJestConfig = require('../utils/createJestConfig'); diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index 4deff936370..f6aac47caa9 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 ``](#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,13 +30,21 @@ 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 a Node Backend](#integrating-with-a-node-backend) +- [Integrating with an API Backend](#integrating-with-an-api-backend) + - [Node](#node) + - [Ruby on Rails](#ruby-on-rails) - [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) @@ -46,13 +57,16 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Coverage Reporting](#coverage-reporting) - [Continuous Integration](#continuous-integration) - [Disabling jsdom](#disabling-jsdom) - - [Experimental Snapshot Testing](#experimental-snapshot-testing) + - [Snapshot Testing](#snapshot-testing) - [Editor Integration](#editor-integration) - [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) @@ -61,9 +75,12 @@ 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) - [`npm run build` silently fails](#npm-run-build-silently-fails) + - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) - [Something Missing?](#something-missing) ## Updating to New Releases @@ -161,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. @@ -181,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 @@ -195,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 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 @@ -330,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 @@ -384,7 +520,7 @@ The `<script>` tag with the compiled code will be added to it automatically duri You can also add other assets to the `public` folder. -Note that we normally we encourage you to `import` assets in JavaScript files instead. +Note that we normally encourage you to `import` assets in JavaScript files instead. For example, see the sections on [adding a stylesheet](#adding-a-stylesheet) and [adding images and fonts](#adding-images-and-fonts). This mechanism provides a number of benefits: @@ -453,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: @@ -475,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,7 +653,14 @@ default you will have `NODE_ENV` defined for you, and any other environment vari variable named `TC_SECRET_CODE` will be exposed in your JS as `process.env.TC_SECRET_CODE`, in addition to `process.env.NODE_ENV`. ->Note: Changing any environment variables will require you to restart the development server if it is running. +**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 `TC_`. 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. + +These environment variables will be defined for you on `process.env`. For example, having an environment +variable named `TC_SECRET_CODE` will be exposed in your JS as `process.env.TC_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. @@ -537,6 +694,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 `TC_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 @@ -545,13 +706,26 @@ if (process.env.NODE_ENV !== 'production') { } ``` -The above form is looking for a variable called `TC_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 `@trunkclub/build@7.0.0` and higher. + +You can also access the environment variables starting with `TC_` 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 `TC_` 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) @@ -562,7 +736,7 @@ set TC_SECRET_CODE=abcdef&&npm start (Note: the lack of whitespace is intentional.) -#### Linux, OS X (Bash) +#### Linux, macOS (Bash) ```bash TC_SECRET_CODE=abcdef npm start @@ -601,9 +775,19 @@ Please refer to these two threads for reference: Create React App will add decorator support when the specification advances to a stable stage. -## Integrating with a Node Backend +## Integrating with an API Backend + +These tutorials will help you to integrate your app with an API backend running on another port, +using `fetch()` to access it. + +### Node +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). -Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/) for instructions on integrating an app with a Node backend running on another port, and using `fetch()` to access it. 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/). +You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo-rails). ## Proxying API Requests in Development @@ -636,7 +820,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)). @@ -658,7 +842,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 @@ -674,14 +858,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.
@@ -738,8 +947,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 @@ -765,7 +974,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 @@ -797,9 +1006,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). @@ -886,7 +1113,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 @@ -927,17 +1154,15 @@ In contrast, **jsdom is not needed** for the following APIs: * [`TestUtils.createRenderer()`](https://facebook.github.io/react/docs/test-utils.html#shallow-rendering) (shallow rendering) * [`shallow()`](http://airbnb.io/enzyme/docs/api/shallow.html) in [Enzyme](http://airbnb.io/enzyme/index.html) -Finally, jsdom is also not needed for [snapshot testing](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html). Longer term, this is the direction we are interested in exploring, but snapshot testing is [not fully baked yet](https://github.com/facebookincubator/create-react-app/issues/372) so we don’t officially encourage its usage yet. +Finally, jsdom is also not needed for [snapshot testing](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html). -### Experimental Snapshot Testing +### Snapshot Testing -Snapshot testing is a new feature of Jest that automatically generates text snapshots of your components and saves them on the disk so if the UI output changes, you get notified without manually writing any assertions on the component output. - -This feature is experimental and still [has major usage issues](https://github.com/facebookincubator/create-react-app/issues/372) so we only encourage you to use it if you like experimental technology. We intend to gradually improve it over time and eventually offer it as the default solution for testing React components, but this will take time. [Read more about snapshot testing.](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) +Snapshot testing is a feature of Jest that automatically generates text snapshots of your components and saves them on the disk so if the UI output changes, you get notified without manually writing any assertions on the component output. [Read more about snapshot testing.](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) ### 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) @@ -952,7 +1177,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) @@ -987,30 +1212,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 +npm install -g serve +serve -s build +``` + +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 -cd build -python -m SimpleHTTPServer 9000 +serve -h ``` -If you're using [Node](https://nodejs.org/) and [Express](http://expressjs.com/) as a server, it might look like this: +### 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. @@ -1021,14 +1264,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 @@ -1042,12 +1296,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 @@ -1131,17 +1400,18 @@ To publish it at [https://myusername.github.io/my-app](https://myusername.github npm install --save-dev gh-pages ``` -Add the following script in your `package.json`: +Add the following scripts in your `package.json`: ```js // ... "scripts": { // ... - "deploy": "npm run build&&gh-pages -d build" + "predeploy": "npm run build", + "deploy": "gh-pages -d build" } ``` -(Note: the lack of whitespace is intentional.) +The `predeploy` script will run automatically before `deploy` is run. #### Step 3: Deploy the site by running `npm run deploy` @@ -1151,7 +1421,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: @@ -1163,7 +1433,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). @@ -1173,13 +1443,46 @@ GitHub Pages doesn't support routers that use the HTML5 `pushState` history API 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). +#### Resolving Heroku Deployment Errors + +Sometimes `npm run build` works locally but fails during deploy via Heroku. Following are the most common cases. + +##### "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 +``` + +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 @@ -1208,7 +1511,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 @@ -1216,27 +1539,45 @@ 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 + +When you save a file while `npm start` is running, the browser should refresh with the updated code.
+If this doesn’t happen, try one of the following workarounds: + +* If your project is in a Dropbox folder, try moving it out. +* 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 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). + ### `npm test` hangs on macOS Sierra If you run `npm test` and the console gets stuck after printing `react-scripts test --env=jsdom` to the console there might be a problem with your [Watchman](https://facebook.github.io/watchman/) installation as described in [facebookincubator/create-react-app#713](https://github.com/facebookincubator/create-react-app/issues/713). @@ -1257,7 +1598,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. @@ -1265,6 +1606,11 @@ There are also reports that *uninstalling* Watchman fixes the issue. So if nothi It is reported that `npm run build` can fail on machines with no swap space, which is common in cloud environments. If [the symptoms are matching](https://github.com/facebookincubator/create-react-app/issues/1133#issuecomment-264612171), consider adding some swap space to the machine you’re building on, or build the project locally. +### `npm run build` fails on Heroku + +This may be a problem with case sensitive filenames. +Please refer to [this section](#resolving-heroku-deployment-errors). + ## Something Missing? If you have ideas for more “How To” recipes that should be on this page, [let us know](https://github.com/facebookincubator/create-react-app/issues) or [contribute some!](https://github.com/facebookincubator/create-react-app/edit/master/packages/react-scripts/template/README.md) diff --git a/packages/react-scripts/template/gitignore b/packages/react-scripts/template/gitignore index 6c96c5cff12..927d17bb9c5 100644 --- a/packages/react-scripts/template/gitignore +++ b/packages/react-scripts/template/gitignore @@ -1,15 +1,18 @@ -# 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 +/node_modules # testing -coverage +/coverage # production -build +/build # 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 e43207e3c04..bbceef59a5a 100644 --- a/packages/react-scripts/utils/createJestConfig.js +++ b/packages/react-scripts/utils/createJestConfig.js @@ -7,15 +7,17 @@ * 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 pathExists = require('path-exists'); +const fs = require('fs'); const paths = require('../config/paths'); module.exports = (resolve, rootDir, isEjecting) => { // Use this instead of `paths.testsSetup` to avoid putting // an absolute filename into configuration after ejecting. - const setupTestsFile = pathExists.sync(paths.testsSetup) ? '/src/setupTests.js' : undefined; + const setupTestsFile = fs.existsSync(paths.testsSetup) ? '/src/setupTests.js' : undefined; // TODO: I don't know if it's safe or not to just use / as path separator // in Jest configs. We need help from somebody with Windows to determine this. @@ -35,7 +37,7 @@ module.exports = (resolve, rootDir, isEjecting) => { setupFiles: [require.resolve('babel-polyfill')], setupTestFrameworkScriptFile: setupTestsFile, testPathIgnorePatterns: [ - '[/\\\\](build|docs|node_modules)[/\\\\]' + '[/\\\\](build|docs|node_modules|scripts)[/\\\\]' ], testEnvironment: 'node', testURL: 'http://localhost', 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.sh b/tasks/e2e-installs.sh similarity index 52% rename from tasks/e2e.sh rename to tasks/e2e-installs.sh index 5a2ecdf31ef..cacf4f96e1f 100755 --- a/tasks/e2e.sh +++ b/tasks/e2e-installs.sh @@ -21,10 +21,8 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` function cleanup { echo 'Cleaning up.' - 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 + cd "$root_path" + rm -rf "$temp_cli_path" "$temp_app_path" } # Error messages are redirected to stderr @@ -41,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 $* } @@ -60,175 +65,123 @@ root_path=$PWD npm install -# 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 ] -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 - 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 ./ - -# ****************************************************************************** -# First, test the create-react-app development environment. -# This does not affect our users but makes sure we can develop it. -# ****************************************************************************** - -# 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 - -# 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 - -# Test local start command -npm start -- --smoke-test - # ****************************************************************************** -# Next, pack react-scripts and create-react-app so we can verify they work. +# First, pack and install create-react-app. # ****************************************************************************** # 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 - -# 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 - -# Finally, pack react-scripts -scripts_path=$root_path/packages/react-scripts/`npm pack` - -# Restore package.json -rm package.json -mv package.json.orig package.json +# Install the CLI in a temporary location +cd "$temp_cli_path" +npm install "$cli_path" # ****************************************************************************** -# Now that we have packed them, create a clean app folder and install them. +# Test --scripts-version with a version number # ****************************************************************************** -# Install the CLI in a temporary location -cd $temp_cli_path -npm install $cli_path +cd "$temp_app_path" +create_react_app --scripts-version=0.4.0 test-app-version-number +cd test-app-version-number -# Install the app in a temporary location -cd $temp_app_path -create_react_app --scripts-version=$scripts_path test-app +# Check corresponding scripts version is installed. +exists node_modules/react-scripts +grep '"version": "0.4.0"' node_modules/react-scripts/package.json # ****************************************************************************** -# 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. +# Test --scripts-version with a tarball url # ****************************************************************************** -# 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 - -# 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 +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 -# Test the server -npm start -- --smoke-test +# Check corresponding scripts version is installed. +exists node_modules/react-scripts +grep '"version": "0.4.0"' node_modules/react-scripts/package.json # ****************************************************************************** -# Finally, let's check that everything still works after ejecting. +# Test --scripts-version with a custom fork of react-scripts # ****************************************************************************** -# 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 +cd "$temp_app_path" +create_react_app --scripts-version=react-scripts-fork test-app-fork +cd test-app-fork -# 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 +# Check corresponding scripts version is installed. +exists node_modules/react-scripts-fork -# Run tests, overring the watch option to disable it. -# `CI=true npm test` won't work here because `npm test` becomes just `jest`. -# We should either teach Jest to respect CI env variable, or make -# `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 +# ****************************************************************************** +# Test project folder is deleted on failing package installation +# ****************************************************************************** -# Test the server -npm start -- --smoke-test +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 --scripts-version with a version number +# Test project folder is not deleted when creating app over existing folder # ****************************************************************************** -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 -grep '"version": "0.4.0"' node_modules/react-scripts/package.json +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 tarball url +# Test --scripts-version with a scoped fork tgz of react-scripts # ****************************************************************************** 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 +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. -test -e node_modules/react-scripts -grep '"version": "0.4.0"' node_modules/react-scripts/package.json +exists node_modules/@enoah_netzach/react-scripts # ****************************************************************************** -# Test --scripts-version with a custom fork of react-scripts +# Test nested folder path as the project name # ****************************************************************************** -cd $temp_app_path -create_react_app --scripts-version=react-scripts-fork test-app-fork -cd test-app-fork +#Testing a path that exists +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 +create_react_app test-app-nested-paths-t1/aa/bb/cc/dd +cd test-app-nested-paths-t1/aa/bb/cc/dd +npm start -- --smoke-test -# Check corresponding scripts version is installed. -test -e node_modules/react-scripts-fork +#Testing a path that does not exist +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" +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 +npm start -- --smoke-test # Cleanup cleanup diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh new file mode 100755 index 00000000000..0d9c8372bcb --- /dev/null +++ b/tasks/e2e-kitchensink.sh @@ -0,0 +1,245 @@ +#!/bin/bash +# 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. + +# ****************************************************************************** +# This is an end-to-end kitchensink test intended to run on CI. +# You can also run it locally but it's slow. +# ****************************************************************************** + +# Start in tasks/ even if run from root directory +cd "$(dirname "$0")" + +# 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.' + 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 +function handle_error { + echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2; + cleanup + echo 'Exiting with error.' 1>&2; + exit 1 +} + +function handle_exit { + cleanup + echo 'Exiting without error.' 1>&2; + exit +} + +function create_react_app { + 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 +trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR + +# Cleanup before exit on any termination signal +trap 'set +x; handle_exit' SIGQUIT SIGTERM SIGINT SIGKILL SIGHUP + +# Echo every command being executed +set -x + +# Go to root +cd .. +root_path=$PWD + +npm install + +if [ "$USE_YARN" = "yes" ] +then + # Install Yarn so that the test can use it to install packages. + npm install -g yarn + yarn cache clean +fi + +# ****************************************************************************** +# First, pack react-scripts and create-react-app so we can use them. +# ****************************************************************************** + +# Pack CLI +cd "$root_path"/packages/create-react-app +cli_path=$PWD/`npm pack` + +# Go to 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 + +# Finally, pack react-scripts +scripts_path="$root_path"/packages/react-scripts/`npm pack` + +# Restore package.json +rm package.json +mv package.json.orig package.json + +# ****************************************************************************** +# Now that we have packed them, create a clean app folder and install them. +# ****************************************************************************** + +# Install the CLI in a temporary location +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 + +# 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, +# let's make sure all npm scripts are in the working state. +# ****************************************************************************** + +# Enter the app directory +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 +REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + PUBLIC_URL=http://www.example.org/spa/ \ + npm run build + +# Check for expected output +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 +tmp_server_log=`mktemp` +PORT=3001 \ + REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + nohup npm start &>$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_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 + +# ...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 +REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + PUBLIC_URL=http://www.example.org/spa/ \ + npm run build + +# Check for expected output +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 +tmp_server_log=`mktemp` +PORT=3002 \ + REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + nohup npm start &>$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=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_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 +cleanup diff --git a/tasks/e2e-simple.sh b/tasks/e2e-simple.sh new file mode 100755 index 00000000000..d733656d866 --- /dev/null +++ b/tasks/e2e-simple.sh @@ -0,0 +1,281 @@ +#!/bin/bash +# 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. + +# ****************************************************************************** +# This is an end-to-end test intended to run on CI. +# You can also run it locally but it's slow. +# ****************************************************************************** + +# Start in tasks/ even if run from root directory +cd "$(dirname "$0")" + +# CLI and app 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'` + +function cleanup { + echo 'Cleaning up.' + 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 +} + +# Error messages are redirected to stderr +function handle_error { + echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2; + cleanup + echo 'Exiting with error.' 1>&2; + exit 1 +} + +function handle_exit { + cleanup + echo 'Exiting without error.' 1>&2; + exit +} + +function create_react_app { + 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 +trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR + +# Cleanup before exit on any termination signal +trap 'set +x; handle_exit' SIGQUIT SIGTERM SIGINT SIGKILL SIGHUP + +# Echo every command being executed +set -x + +# Go to root +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 ]] +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 + yarn cache clean +fi + +# Lint own code +./node_modules/.bin/eslint --max-warnings 0 . + +# ****************************************************************************** +# First, test the create-react-app development environment. +# This does not affect our users but makes sure we can develop it. +# ****************************************************************************** + +# Test local build command +npm run build +# Check for expected output +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: +# exists template/src/__snapshots__/App.test.js.snap + +# Test local start command +npm start -- --smoke-test + +# ****************************************************************************** +# Next, pack react-scripts and create-react-app so we can verify they work. +# ****************************************************************************** + +# Pack CLI +cd "$root_path"/packages/create-react-app +cli_path=$PWD/`npm pack` + +# Go to 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 + +# Finally, pack react-scripts +scripts_path="$root_path"/packages/react-scripts/`npm pack` + +# Restore package.json +rm package.json +mv package.json.orig package.json + +# ****************************************************************************** +# Now that we have packed them, create a clean app folder and install them. +# ****************************************************************************** + +# Install the CLI in a temporary location +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 + +# ****************************************************************************** +# 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 +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: +# 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. +# ****************************************************************************** + +# 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 + +# Test the build +npm run build +# Check for expected output +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`. +# We should either teach Jest to respect CI env variable, or make +# `scripts/test.js` survive ejection (right now it doesn't). +npm test -- --watch=no +# Uncomment when snapshot testing is enabled by default: +# 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 99f844e7fb0..49a7302852f 100755 --- a/tasks/release.sh +++ b/tasks/release.sh @@ -39,8 +39,6 @@ if [ -n "$(git status --porcelain)" ]; then exit 1; fi - -# Update deps -cd packages/react-scripts - -node ./node_modules/.bin/publish "$@" +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!