diff --git a/.gitignore b/.gitignore
index 0aa0ed568ba..79ce88915d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+.idea/
+.vscode/
node_modules/
build
.DS_Store
diff --git a/.travis.yml b/.travis.yml
index aa61d9eb976..524224e9f6b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,7 @@ cache:
- node_modules
- packages/create-react-app/node_modules
- packages/react-scripts/node_modules
+install: true
script:
- 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
- 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
@@ -21,5 +22,7 @@ env:
- 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 5d4225d0755..2391b014ee7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,199 @@
+## 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.
+
+
+
+#### :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.
@@ -7,23 +203,23 @@ Thanks to [@Timer](/~https://github.com/timer) for cutting this release.
* `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`
@@ -31,7 +227,7 @@ Thanks to [@Timer](/~https://github.com/timer) for cutting this release.
* [#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`
@@ -45,7 +241,7 @@ Thanks to [@Timer](/~https://github.com/timer) for cutting this release.
* `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))
@@ -110,7 +306,7 @@ Thanks to [@Timer](/~https://github.com/timer) for cutting this release.
* [#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))
@@ -197,60 +393,60 @@ npm install -g create-react-app@1.0.3
* `react-scripts`
* [#1233](/~https://github.com/facebookincubator/create-react-app/pull/1233) Disable subresource integrity temporarily. ([@Timer](/~https://github.com/Timer))
-
+
We added [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) checks to the build output in 0.8.2 but it turns out that they may fail in browsers using special compression proxies, such as Chrome on Android, when served over HTTP. We disabled the checks until we can find a safe way to add them.
-
+
* `react-dev-utils`
* [#1226](/~https://github.com/facebookincubator/create-react-app/pull/1226) Fix weird lint output. ([@n3tr](/~https://github.com/n3tr))
-
+
Fixes strange lint message formatting in some edge cases.
-
+
* [#1215](/~https://github.com/facebookincubator/create-react-app/pull/1215) Fix - openChrome won't open default browser (using Canary). ([@n3tr](/~https://github.com/n3tr))
-
+
Fixes a regression that caused stable Google Chrome to be opened even if you are using Canary as the default browser.
-
+
* `create-react-app`
* [#1223](/~https://github.com/facebookincubator/create-react-app/pull/1223) Clean up Yarn detection and install code. ([@fson](/~https://github.com/fson))
-
+
Fixes noisy output on Windows when Yarn is not installed.
* [#1224](/~https://github.com/facebookincubator/create-react-app/pull/1224) Exit with an error code when npm/yarn install fails. ([@fson](/~https://github.com/fson))
-
+
#### :nail_care: Enhancement
* `react-scripts`
* [#1237](/~https://github.com/facebookincubator/create-react-app/pull/1237) Clear scrollback in test mode. ([@gaearon](/~https://github.com/gaearon))
-
+
Ensures test watcher clears the console before running.
-
+
* [#1229](/~https://github.com/facebookincubator/create-react-app/pull/1229) Disable jest watch mode when --coverage flag is present [#1207]. ([@BenoitAverty](/~https://github.com/BenoitAverty))
-
+
Since coverage doesn't work well with watch mode, we don’t run the watcher on `npm test -- --coverage` anymore.
-
+
* [#1212](/~https://github.com/facebookincubator/create-react-app/pull/1212) Proxy rewrites Origin header to match the target server URL. ([@koles](/~https://github.com/koles))
-
+
Makes sure more API endpoints can work with the `proxy` setting.
-
+
* [#1222](/~https://github.com/facebookincubator/create-react-app/pull/1222) Disable gh-page setup instruction if scripts.deploy has been added. ([@n3tr](/~https://github.com/n3tr))
-
+
Suppresses the instructions printed at the end of `npm run build` if `npm run deploy` already exists.
* `create-react-app`
* [#1236](/~https://github.com/facebookincubator/create-react-app/pull/1236) Tweak console messages. ([@gaearon](/~https://github.com/gaearon))
-
+
Makes error messages more friendly.
-
+
* [#1195](/~https://github.com/facebookincubator/create-react-app/pull/1195) Use "commander" for cli argv handling. ([@EnoahNetzach](/~https://github.com/EnoahNetzach))
-
+
Adds `create-react-app --help` with a list of options.
* `react-dev-utils`
* [#1211](/~https://github.com/facebookincubator/create-react-app/pull/1211) Use a better clear console sequence. ([@gaearon](/~https://github.com/gaearon))
-
+
Ensures the development server clears the terminal when files are changed.
#### :memo: Documentation
@@ -300,14 +496,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
@@ -771,7 +967,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`)
@@ -913,7 +1109,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 b6c4e69bbfd..3a6402606db 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
diff --git a/README.md b/README.md
index 52843b572ed..87ddd4ee5d1 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,9 @@ 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.
+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).
+
## tl;dr
```sh
@@ -13,7 +16,6 @@ npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
-
```
Then open [http://localhost:3000/](http://localhost:3000/) to see your app.
@@ -70,7 +72,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,20 +82,21 @@ You will see the build errors and lint warnings in the console.
-### `npm test`
+### `npm test` or `yarn test`
Runs the test watcher in an interactive mode.
By default, runs tests related to files changes since the last commit.
[Read more about testing.](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests)
-### `npm run build`
+### `npm run build` or `yarn build`
Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
-Your app is ready to be deployed!
+A [service worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) using an [offline-first caching strategy](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network) is automatically generated.
+Your ([progressive web](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app)) app is ready to be deployed!
## User Guide
@@ -102,13 +105,16 @@ The [User Guide](/~https://github.com/facebookincubator/create-react-app/blob/mast
- [Updating to New Releases](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#updating-to-new-releases)
- [Folder Structure](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#folder-structure)
- [Available Scripts](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#available-scripts)
+- [Supported Language Features and Polyfills](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#supported-language-features-and-polyfills)
- [Syntax Highlighting in the Editor](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#syntax-highlighting-in-the-editor)
- [Displaying Lint Output in the Editor](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#displaying-lint-output-in-the-editor)
+- [Debugging in the Editor](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#debugging-in-the-editor)
- [Changing the Page ``](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#changing-the-page-title)
- [Installing a Dependency](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#installing-a-dependency)
- [Importing a Component](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#importing-a-component)
- [Adding a Stylesheet](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-stylesheet)
- [Post-Processing CSS](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#post-processing-css)
+- [Adding a CSS Preprocessor (Sass, Less etc.)](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc)
- [Adding Images and Fonts](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-images-and-fonts)
- [Using the `public` Folder](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-the-public-folder)
- [Using Global Variables](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-global-variables)
@@ -120,6 +126,7 @@ The [User Guide](/~https://github.com/facebookincubator/create-react-app/blob/mast
- [Proxying API Requests in Development](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development)
- [Using HTTPS in Development](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-https-in-development)
- [Generating Dynamic `` 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)
@@ -151,6 +158,7 @@ Please refer to the [User Guide](/~https://github.com/facebookincubator/create-rea
* Import CSS and image files directly from JavaScript.
* Autoprefixed CSS, so you don’t need `-webkit` or other prefixes.
* A `build` script to bundle JS, CSS, and images for production, with sourcemaps.
+* An offline-first [service worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) and a [web app manifest](https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/), meeting all the [Progressive Web App](/~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) criteria.
**The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything.
@@ -173,7 +181,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.
@@ -210,7 +218,7 @@ If you don’t agree with the choices made in this project, you might want to ex
Some of the more popular and actively maintained ones are:
* [insin/nwb](/~https://github.com/insin/nwb)
-* [mozilla/neo](/~https://github.com/mozilla/neo)
+* [mozilla-neutrino/neutrino-dev](/~https://github.com/mozilla-neutrino/neutrino-dev)
* [NYTimes/kyt](/~https://github.com/NYTimes/kyt)
* [zeit/next.js](/~https://github.com/zeit/next.js)
* [gatsbyjs/gatsby](/~https://github.com/gatsbyjs/gatsby)
diff --git a/appveyor.cleanup-cache.txt b/appveyor.cleanup-cache.txt
new file mode 100644
index 00000000000..0656d74ac41
--- /dev/null
+++ b/appveyor.cleanup-cache.txt
@@ -0,0 +1,6 @@
+Edit this file to trigger a cache rebuild.
+http://help.appveyor.com/discussions/questions/1310-delete-cache
+
+----
+Just testing if this works.
+Hello, world.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 00000000000..bc2ac1b3f88
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,40 @@
+environment:
+ matrix:
+ - 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:
+ - 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 f648aac48a1..e2429777df4 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "lerna": "2.0.0-beta.37",
+ "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 a9262568d25..1398c949cf9 100644
--- a/package.json
+++ b/package.json
@@ -11,14 +11,14 @@
"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.37",
+ "babel-eslint": "7.1.0",
+ "eslint": "3.16.1",
+ "eslint-config-react-app": "0.5.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",
+ "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).
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 8a42f885e31..3760ec4e1ae 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
@@ -82,7 +75,10 @@ if (env === 'test') {
// JSX, Flow
require.resolve('babel-preset-react')
],
- plugins: plugins
+ plugins: plugins.concat([
+ // Compiles import() to a deferred require()
+ require.resolve('babel-plugin-dynamic-import-node')
+ ])
};
} else {
module.exports = {
@@ -102,6 +98,8 @@ if (env === 'test') {
// Async functions are converted to generators by babel-preset-latest
async: false
}],
+ // Adds syntax support for import()
+ require.resolve('babel-plugin-syntax-dynamic-import'),
])
};
diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json
index ae5ab93be65..049d75db3ee 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.1.0",
+ "version": "2.1.1",
"description": "Babel preset used by Create React App",
"repository": "facebookincubator/create-react-app",
"license": "BSD-3-Clause",
@@ -11,8 +11,9 @@
"index.js"
],
"dependencies": {
+ "babel-plugin-dynamic-import-node": "1.0.0",
+ "babel-plugin-syntax-dynamic-import": "6.18.0",
"babel-plugin-transform-class-properties": "6.22.0",
- "babel-plugin-transform-es2015-parameters": "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",
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).
Please refer to its documentation:
* [Getting Started](/~https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app.
diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js
old mode 100644
new mode 100755
index 4dbb37f4969..5c2205dc6fc
--- a/packages/create-react-app/index.js
+++ b/packages/create-react-app/index.js
@@ -39,8 +39,9 @@
'use strict';
var chalk = require('chalk');
+var validateProjectName = require("validate-npm-package-name");
-var currentNodeVersion = process.versions.node
+var currentNodeVersion = process.versions.node;
if (currentNodeVersion.split('.')[0] < 4) {
console.error(
chalk.red(
@@ -58,6 +59,7 @@ var path = require('path');
var execSync = require('child_process').execSync;
var spawn = require('cross-spawn');
var semver = require('semver');
+var dns = require('dns');
var projectName;
@@ -97,6 +99,14 @@ if (typeof projectName === 'undefined') {
process.exit(1);
}
+function printValidationResults(results) {
+ if (typeof results !== 'undefined') {
+ results.forEach(function (error) {
+ console.error(chalk.red(' * ' + error));
+ });
+ }
+}
+
var hiddenProgram = new commander.Command()
.option('--internal-testing-template ', '(internal usage only, DO NOT RELY ON THIS) ' +
'use a non-standard application template')
@@ -124,7 +134,7 @@ function createApp(name, verbose, version, template) {
var packageJson = {
name: appName,
version: '0.1.0',
- private: true,
+ private: true
};
fs.writeFileSync(
path.join(root, 'package.json'),
@@ -133,10 +143,6 @@ function createApp(name, verbose, version, template) {
var originalDirectory = process.cwd();
process.chdir(root);
- console.log('Installing packages. This might take a couple minutes.');
- console.log('Installing ' + chalk.cyan('react-scripts') + '...');
- console.log();
-
run(root, appName, version, verbose, originalDirectory, template);
}
@@ -149,24 +155,47 @@ function shouldUseYarn() {
}
}
-function install(packageToInstall, verbose, callback) {
- var command;
- var args;
- if (shouldUseYarn()) {
- command = 'yarnpkg';
- args = [ 'add', '--dev', '--exact', packageToInstall];
- } else {
- command = 'npm';
- args = ['install', '--save-dev', '--save-exact', packageToInstall];
- }
+function install(useYarn, dependencies, verbose, isOnline) {
+ return new Promise(function(resolve, reject) {
+ var command;
+ var args;
+ if (useYarn) {
+ command = 'yarnpkg';
+ args = [
+ 'add',
+ '--exact',
+ ];
+ if (!isOnline) {
+ args.push('--offline');
+ }
+ [].push.apply(args, dependencies);
+
+ if (!isOnline) {
+ console.log(chalk.yellow('You appear to be offline.'));
+ console.log(chalk.yellow('Falling back to the local Yarn cache.'));
+ console.log();
+ }
+
+ } else {
+ checkNpmVersion();
+ command = 'npm';
+ args = ['install', '--save', '--save-exact'].concat(dependencies);
+ }
- if (verbose) {
- args.push('--verbose');
- }
+ if (verbose) {
+ args.push('--verbose');
+ }
- var child = spawn(command, args, {stdio: 'inherit'});
- child.on('close', function(code) {
- callback(code, command, args);
+ var child = spawn(command, args, {stdio: 'inherit'});
+ child.on('close', function(code) {
+ if (code !== 0) {
+ reject({
+ command: command + ' ' + args.join(' ')
+ });
+ return;
+ }
+ resolve();
+ });
});
}
@@ -174,24 +203,74 @@ function run(root, appName, version, verbose, originalDirectory, template) {
var packageToInstall = getInstallPackage(version);
var packageName = getPackageName(packageToInstall);
- install(packageToInstall, verbose, function(code, command, args) {
- if (code !== 0) {
- console.error(chalk.cyan(command + ' ' + args.join(' ')) + ' failed');
- process.exit(1);
- }
-
- checkNodeVersion(packageName);
+ var allDependencies = ['react', 'react-dom', packageToInstall];
- var scriptsPath = path.resolve(
- process.cwd(),
- 'node_modules',
- packageName,
- 'scripts',
- 'init.js'
- );
- var init = require(scriptsPath);
- init(root, appName, verbose, originalDirectory, template);
- });
+ console.log('Installing packages. This might take a couple minutes.');
+ console.log(
+ 'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') +
+ ', and ' + chalk.cyan(packageName) + '...'
+ );
+ console.log();
+
+ var useYarn = shouldUseYarn();
+ checkIfOnline(useYarn)
+ .then(function(isOnline) {
+ return install(useYarn, allDependencies, verbose, isOnline);
+ })
+ .then(function() {
+ 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) {
@@ -212,6 +291,11 @@ function getPackageName(installPackage) {
// 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];
+ } 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 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];
@@ -219,6 +303,25 @@ function getPackageName(installPackage) {
return installPackage;
}
+function checkNpmVersion() {
+ var isNpm2 = false;
+ try {
+ var npmVersion = execSync('npm --version').toString();
+ isNpm2 = semver.lt(npmVersion, '3.0.0');
+ } catch (err) {
+ return;
+ }
+ if (!isNpm2) {
+ return;
+ }
+ console.log(chalk.yellow('It looks like you are using npm 2.'));
+ console.log(chalk.yellow(
+ 'We suggest using npm 3 or Yarn for faster install times ' +
+ 'and less disk space usage.'
+ ));
+ console.log();
+}
+
function checkNodeVersion(packageName) {
var packageJsonPath = path.resolve(
process.cwd(),
@@ -246,11 +349,18 @@ function checkNodeVersion(packageName) {
}
function checkAppName(appName) {
+ var validationResult = validateProjectName(appName);
+ if (!validationResult.validForNewPackages) {
+ console.error('Could not create a project called ' + chalk.red('"' + appName + '"') + ' because of npm naming restrictions:');
+ printValidationResults(validationResult.errors);
+ printValidationResults(validationResult.warnings);
+ process.exit(1);
+ }
+
// TODO: there should be a single place that holds the dependencies
var dependencies = ['react', 'react-dom'];
var devDependencies = ['react-scripts'];
var allDependencies = dependencies.concat(devDependencies).sort();
-
if (allDependencies.indexOf(appName) >= 0) {
console.error(
chalk.red(
@@ -268,15 +378,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'
];
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.resolve('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 93df8da55b8..add92d005ca 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.4",
+ "version": "1.2.1",
"keywords": [
"react"
],
@@ -24,6 +24,7 @@
"commander": "^2.9.0",
"cross-spawn": "^4.0.0",
"fs-extra": "^1.0.0",
- "semver": "^5.0.3"
+ "semver": "^5.0.3",
+ "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).
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..f4d33baffa3 100644
--- a/packages/eslint-config-react-app/index.js
+++ b/packages/eslint-config-react-app/index.js
@@ -123,6 +123,7 @@ module.exports = {
'no-this-before-super': 'warn',
'no-throw-literal': 'warn',
'no-undef': 'error',
+ 'no-restricted-globals': ['error', 'event'],
'no-unexpected-multiline': 'warn',
'no-unreachable': 'warn',
'no-unused-expressions': ['warn', {
@@ -133,7 +134,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',
@@ -155,6 +157,15 @@ module.exports = {
'unicode-bom': ['warn', 'never'],
'use-isnan': 'warn',
'valid-typeof': 'warn',
+ 'no-restricted-properties': ['error', {
+ object: 'require',
+ property: 'ensure',
+ message: 'Please use import() instead. More info: https://webpack.js.org/guides/code-splitting-import/#dynamic-import'
+ }, {
+ object: 'System',
+ property: 'import',
+ message: 'Please use import() instead. More info: https://webpack.js.org/guides/code-splitting-import/#dynamic-import'
+ }],
// /~https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/
diff --git a/packages/eslint-config-react-app/package.json b/packages/eslint-config-react-app/package.json
index 87c7e8742f7..9f8db175b2b 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.1",
+ "version": "0.5.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": "^4.0.0",
"eslint-plugin-react": "^6.4.1"
}
}
diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md
index 455996fcc59..2d430e2a299 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).
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`.
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 ` forces a project rebuild.
-We’re not sure why this isn't Webpack's default behavior.
+This Webpack plugin ensures `npm install ` forces a project rebuild.
+We’re not sure why this isn't Webpack's default behavior.
See [#186](/~https://github.com/facebookincubator/create-react-app/issues/186) for details.
```js
@@ -83,8 +83,8 @@ module.exports = {
#### `checkRequiredFiles(files: Array): boolean`
-Makes sure that all passed files exist.
-Filenames are expected to be absolute.
+Makes sure that all passed files exist.
+Filenames are expected to be absolute.
If a file is not found, prints a warning message and returns `false`.
```js
@@ -161,8 +161,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.
+On Mac OS X, attempts to reuse an existing Chrome tab via AppleScript.
Otherwise, falls back to [opn](/~https://github.com/sindresorhus/opn) behavior.
@@ -179,8 +179,8 @@ if (openBrowser('http://localhost:3000')) {
This function displays a console prompt to the user.
-By convention, "no" should be the conservative choice.
-If you mistype the answer, we'll always take it as a "no".
+By convention, "no" should be the conservative choice.
+If you mistype the answer, we'll always take it as a "no".
You can control the behavior on `` with `isYesDefault`.
```js
diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js
index 5540fbad47a..d412b0c7a72 100644
--- a/packages/react-dev-utils/getProcessForPort.js
+++ b/packages/react-dev-utils/getProcessForPort.js
@@ -43,7 +43,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..2e66716439b 100644
--- a/packages/react-dev-utils/openBrowser.js
+++ b/packages/react-dev-utils/openBrowser.js
@@ -17,7 +17,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 +50,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 9870e1ecc34..aae3b83ff01 100644
--- a/packages/react-dev-utils/package.json
+++ b/packages/react-dev-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "react-dev-utils",
- "version": "0.5.0",
+ "version": "0.5.1",
"description": "Webpack utilities used by Create React App",
"repository": "facebookincubator/create-react-app",
"license": "BSD-3-Clause",
@@ -28,7 +28,7 @@
"escape-string-regexp": "1.0.5",
"html-entities": "1.2.0",
"opn": "4.0.2",
- "sockjs-client": "1.0.1",
+ "sockjs-client": "1.1.2",
"strip-ansi": "3.0.1"
}
}
diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js
index 7b1768d8fa0..903861a65d1 100644
--- a/packages/react-dev-utils/webpackHotDevClient.js
+++ b/packages/react-dev-utils/webpackHotDevClient.js
@@ -252,6 +252,10 @@ connection.onmessage = function(e) {
case 'ok':
handleSuccess();
break;
+ case 'content-changed':
+ // Triggered when a file from `contentBase` changed.
+ window.location.reload();
+ break;
case 'warnings':
handleWarnings(message.data);
break;
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).
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/bin/react-scripts.js b/packages/react-scripts/bin/react-scripts.js
index a3ae830f45b..5ed9b244ca6 100755
--- a/packages/react-scripts/bin/react-scripts.js
+++ b/packages/react-scripts/bin/react-scripts.js
@@ -34,5 +34,6 @@ case 'test':
default:
console.log('Unknown script "' + script + '".');
console.log('Perhaps you need to update react-scripts?');
+ console.log('See: /~https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#updating-to-new-releases');
break;
}
diff --git a/packages/react-scripts/config/jest/babelTransform.js b/packages/react-scripts/config/jest/babelTransform.js
index 145bd86cc9a..064fbf10290 100644
--- a/packages/react-scripts/config/jest/babelTransform.js
+++ b/packages/react-scripts/config/jest/babelTransform.js
@@ -1,3 +1,4 @@
+// @remove-file-on-eject
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js
index 7d114080f7c..7ad1fc95c89 100644
--- a/packages/react-scripts/config/paths.js
+++ b/packages/react-scripts/config/paths.js
@@ -91,11 +91,13 @@ module.exports = {
// @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('.'),
+ ownPath: resolveApp('node_modules/react-scripts'),
appBuild: resolveApp('build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
@@ -106,28 +108,33 @@ module.exports = {
testsSetup: resolveApp('src/setupTests.js'),
appNodeModules: resolveApp('node_modules'),
// this is empty with npm3 but node resolution searches higher anyway:
- ownNodeModules: resolveOwn('../node_modules'),
+ ownNodeModules: resolveOwn('node_modules'),
nodePaths: nodePaths,
publicUrl: getPublicUrl(resolveApp('package.json')),
servedPath: getServedPath(resolveApp('package.json'))
};
+var reactScriptsPath = path.resolve('node_modules/react-scripts');
+var reactScriptsLinked = fs.existsSync(reactScriptsPath) && fs.lstatSync(reactScriptsPath).isSymbolicLink();
+
// config before publish: we're in ./packages/react-scripts/config/
-if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) {
+if (!reactScriptsLinked && __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) {
module.exports = {
- appBuild: resolveOwn('../../../build'),
- appPublic: resolveOwn('../template/public'),
- appHtml: resolveOwn('../template/public/index.html'),
- appIndexJs: resolveOwn('../template/src/index.js'),
- appPackageJson: resolveOwn('../package.json'),
- appSrc: resolveOwn('../template/src'),
- yarnLockFile: resolveOwn('../template/yarn.lock'),
- testsSetup: resolveOwn('../template/src/setupTests.js'),
- appNodeModules: resolveOwn('../node_modules'),
- ownNodeModules: resolveOwn('../node_modules'),
+ appPath: resolveApp('.'),
+ ownPath: resolveOwn('.'),
+ 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,
- publicUrl: getPublicUrl(resolveOwn('../package.json')),
- servedPath: getServedPath(resolveOwn('../package.json'))
+ publicUrl: getPublicUrl(resolveOwn('package.json')),
+ servedPath: getServedPath(resolveOwn('package.json'))
};
}
// @remove-on-eject-end
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index e26a5a25d81..642d73741a8 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -106,6 +106,8 @@ module.exports = {
// @remove-on-eject-end
module: {
rules: [
+ // Disable require.ensure as it's not a standard language feature.
+ { parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 86d87d39211..b9ed97f787d 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -15,7 +15,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ManifestPlugin = require('webpack-manifest-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
-var url = require('url');
+var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
var paths = require('./paths');
var getClientEnvironment = require('./env');
@@ -111,6 +111,8 @@ module.exports = {
// @remove-on-eject-end
module: {
rules: [
+ // Disable require.ensure as it's not a standard language feature.
+ { parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
@@ -273,6 +275,13 @@ module.exports = {
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json'
+ }),
+ new SWPrecacheWebpackPlugin({
+ dontCacheBustUrlsMatching: /\.\w{8}\./,
+ filename: 'service-worker.js',
+ minify: true,
+ navigateFallback: publicUrl + '/index.html',
+ staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/]
})
],
// Some libraries import Node modules but don't use them in the browser.
diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js
new file mode 100644
index 00000000000..4d65567bd7d
--- /dev/null
+++ b/packages/react-scripts/config/webpackDevServer.config.js
@@ -0,0 +1,51 @@
+var config = require('./webpack.config.dev');
+var paths = require('./paths');
+
+var protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
+var host = process.env.HOST || 'localhost';
+
+module.exports = {
+ // Enable gzip compression of generated files.
+ compress: true,
+ // Silence WebpackDevServer's own logs since they're generally not useful.
+ // It will still show compile warnings and errors with this setting.
+ clientLogLevel: 'none',
+ // By default WebpackDevServer serves physical files from current directory
+ // in addition to all the virtual build products that it serves from memory.
+ // This is confusing because those files won’t automatically be available in
+ // production build folder unless we copy them. However, copying the whole
+ // 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_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
+ // for files like `favicon.ico`, `manifest.json`, and libraries that are
+ // for some reason broken when imported through Webpack. If you just want to
+ // use an image, put it in `src` and `import` it from JavaScript instead.
+ contentBase: paths.appPublic,
+ // By default files from `contentBase` will not trigger a page reload.
+ watchContentBase: true,
+ // Enable hot reloading server. It will provide /sockjs-node/ endpoint
+ // for the WebpackDevServer client so it can learn when the files were
+ // updated. The WebpackDevServer client is included as an entry point
+ // in the Webpack development configuration. Note that only changes
+ // to CSS are currently hot reloaded. JS changes will refresh the browser.
+ hot: true,
+ // It is important to tell WebpackDevServer to use the same "root" path
+ // as we specified in the config. In development, we always serve from /.
+ publicPath: config.output.publicPath,
+ // WebpackDevServer is noisy by default so we emit custom message instead
+ // by listening to the compiler events with `compiler.plugin` calls above.
+ quiet: true,
+ // Reportedly, this avoids CPU overload on some systems.
+ // /~https://github.com/facebookincubator/create-react-app/issues/293
+ watchOptions: {
+ ignored: /node_modules/
+ },
+ // Enable HTTPS if the HTTPS environment variable is set to 'true'
+ https: protocol === 'https',
+ host: host,
+ overlay: false,
+};
diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js
index 36abe50d87a..587ac5f6f79 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/App.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/App.js
@@ -47,79 +47,79 @@ class App extends Component {
const feature = location.hash.slice(1);
switch (feature) {
case 'array-destructuring':
- require.ensure([], () => this.setFeature(require('./features/syntax/ArrayDestructuring').default));
+ import('./features/syntax/ArrayDestructuring').then(f => this.setFeature(f.default));
break;
case 'array-spread':
- require.ensure([], () => this.setFeature(require('./features/syntax/ArraySpread').default));
+ import('./features/syntax/ArraySpread').then(f => this.setFeature(f.default));
break;
case 'async-await':
- require.ensure([], () => this.setFeature(require('./features/syntax/AsyncAwait').default));
+ import('./features/syntax/AsyncAwait').then(f => this.setFeature(f.default));
break;
case 'class-properties':
- require.ensure([], () => this.setFeature(require('./features/syntax/ClassProperties').default));
+ import('./features/syntax/ClassProperties').then(f => this.setFeature(f.default));
break;
case 'computed-properties':
- require.ensure([], () => this.setFeature(require('./features/syntax/ComputedProperties').default));
+ import('./features/syntax/ComputedProperties').then(f => this.setFeature(f.default));
break;
case 'css-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/CssInclusion').default));
+ import('./features/webpack/CssInclusion').then(f => this.setFeature(f.default));
break;
case 'custom-interpolation':
- require.ensure([], () => this.setFeature(require('./features/syntax/CustomInterpolation').default));
+ import('./features/syntax/CustomInterpolation').then(f => this.setFeature(f.default));
break;
case 'default-parameters':
- require.ensure([], () => this.setFeature(require('./features/syntax/DefaultParameters').default));
+ import('./features/syntax/DefaultParameters').then(f => this.setFeature(f.default));
break;
case 'destructuring-and-await':
- require.ensure([], () => this.setFeature(require('./features/syntax/DestructuringAndAwait').default));
+ import('./features/syntax/DestructuringAndAwait').then(f => this.setFeature(f.default));
break;
case 'file-env-variables':
- require.ensure([], () => this.setFeature(require('./features/env/FileEnvVariables').default));
+ import('./features/env/FileEnvVariables').then(f => this.setFeature(f.default));
break;
case 'generators':
- require.ensure([], () => this.setFeature(require('./features/syntax/Generators').default));
+ import('./features/syntax/Generators').then(f => this.setFeature(f.default));
break;
case 'image-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/ImageInclusion').default));
+ import('./features/webpack/ImageInclusion').then(f => this.setFeature(f.default));
break;
case 'json-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/JsonInclusion').default));
+ import('./features/webpack/JsonInclusion').then(f => this.setFeature(f.default));
break;
case 'node-path':
- require.ensure([], () => this.setFeature(require('./features/env/NodePath').default));
+ import('./features/env/NodePath').then(f => this.setFeature(f.default));
break;
case 'no-ext-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/NoExtInclusion').default));
+ import('./features/webpack/NoExtInclusion').then(f => this.setFeature(f.default));
break;
case 'object-destructuring':
- require.ensure([], () => this.setFeature(require('./features/syntax/ObjectDestructuring').default));
+ import('./features/syntax/ObjectDestructuring').then(f => this.setFeature(f.default));
break;
case 'object-spread':
- require.ensure([], () => this.setFeature(require('./features/syntax/ObjectSpread').default));
+ import('./features/syntax/ObjectSpread').then(f => this.setFeature(f.default));
break;
case 'promises':
- require.ensure([], () => this.setFeature(require('./features/syntax/Promises').default));
+ import('./features/syntax/Promises').then(f => this.setFeature(f.default));
break;
case 'public-url':
- require.ensure([], () => this.setFeature(require('./features/env/PublicUrl').default));
+ import('./features/env/PublicUrl').then(f => this.setFeature(f.default));
break;
case 'rest-and-default':
- require.ensure([], () => this.setFeature(require('./features/syntax/RestAndDefault').default));
+ import('./features/syntax/RestAndDefault').then(f => this.setFeature(f.default));
break;
case 'rest-parameters':
- require.ensure([], () => this.setFeature(require('./features/syntax/RestParameters').default));
+ import('./features/syntax/RestParameters').then(f => this.setFeature(f.default));
break;
case 'shell-env-variables':
- require.ensure([], () => this.setFeature(require('./features/env/ShellEnvVariables').default));
+ import('./features/env/ShellEnvVariables').then(f => this.setFeature(f.default));
break;
case 'svg-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/SvgInclusion').default));
+ import('./features/webpack/SvgInclusion').then(f => this.setFeature(f.default));
break;
case 'template-interpolation':
- require.ensure([], () => this.setFeature(require('./features/syntax/TemplateInterpolation').default));
+ import('./features/syntax/TemplateInterpolation').then(f => this.setFeature(f.default));
break;
case 'unknown-ext-inclusion':
- require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default));
+ import('./features/webpack/UnknownExtInclusion').then(f => this.setFeature(f.default));
break;
default: throw new Error(`Missing feature "${feature}"`);
}
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
index 36c5984a4b5..3783cca0a54 100644
--- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js
@@ -1,12 +1,13 @@
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);
+ return import('./Promises').then(({ default: Promises }) => {
+ return new Promise(resolve => {
+ ReactDOM.render(, div);
+ });
});
});
});
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 371b952c8c5..3b3711e172c 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -1,6 +1,6 @@
{
"name": "react-scripts",
- "version": "0.9.0",
+ "version": "0.9.3",
"description": "Configuration and scripts for Create React App.",
"repository": "facebookincubator/create-react-app",
"license": "BSD-3-Clause",
@@ -23,12 +23,12 @@
"react-scripts": "./bin/react-scripts.js"
},
"dependencies": {
- "autoprefixer": "6.7.2",
+ "autoprefixer": "6.7.5",
"babel-core": "6.22.1",
"babel-eslint": "7.1.1",
"babel-jest": "18.0.0",
- "babel-loader": "6.2.10",
- "babel-preset-react-app": "^2.1.0",
+ "babel-loader": "6.3.2",
+ "babel-preset-react-app": "^2.1.1",
"babel-runtime": "^6.20.0",
"case-sensitive-paths-webpack-plugin": "1.1.4",
"chalk": "1.1.3",
@@ -37,14 +37,14 @@
"css-loader": "0.26.1",
"detect-port": "1.0.1",
"dotenv": "2.0.0",
- "eslint": "3.8.1",
- "eslint-config-react-app": "^0.5.1",
- "eslint-loader": "1.6.1",
+ "eslint": "3.16.1",
+ "eslint-config-react-app": "^0.5.2",
+ "eslint-loader": "1.6.3",
"eslint-plugin-flowtype": "2.21.0",
"eslint-plugin-import": "2.0.1",
- "eslint-plugin-jsx-a11y": "2.2.3",
+ "eslint-plugin-jsx-a11y": "4.0.0",
"eslint-plugin-react": "6.4.1",
- "extract-text-webpack-plugin": "2.0.0-rc.3",
+ "extract-text-webpack-plugin": "2.0.0",
"file-loader": "0.10.0",
"filesize": "3.3.0",
"fs-extra": "0.30.0",
@@ -53,15 +53,16 @@
"http-proxy-middleware": "0.17.3",
"jest": "18.1.0",
"object-assign": "4.1.1",
- "postcss-loader": "1.2.2",
+ "postcss-loader": "1.3.1",
"promise": "7.1.1",
- "react-dev-utils": "^0.5.0",
- "recursive-readdir": "2.1.0",
+ "react-dev-utils": "^0.5.1",
+ "recursive-readdir": "2.1.1",
"strip-ansi": "3.0.1",
"style-loader": "0.13.1",
+ "sw-precache-webpack-plugin": "^0.9.0",
"url-loader": "0.5.7",
"webpack": "2.2.1",
- "webpack-dev-server": "2.3.0",
+ "webpack-dev-server": "2.4.1",
"webpack-manifest-plugin": "1.1.0",
"whatwg-fetch": "2.0.2"
},
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index 7c953c3544e..6892fcd6aff 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -133,7 +133,16 @@ function printErrors(summary, errors) {
// Create the production build and print the deployment instructions.
function build(previousSizeMap) {
console.log('Creating an optimized production build...');
- webpack(config).run((err, stats) => {
+
+ var compiler;
+ try {
+ compiler = webpack(config);
+ } catch (err) {
+ printErrors('Failed to compile.', [err]);
+ process.exit(1);
+ }
+
+ compiler.run((err, stats) => {
if (err) {
printErrors('Failed to compile.', [err]);
process.exit(1);
@@ -214,7 +223,8 @@ function build(previousSizeMap) {
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.');
+ var build = path.relative(process.cwd(), paths.appBuild);
+ console.log('The ' + chalk.cyan(build) + ' folder is ready to be deployed.');
console.log('You may also serve it locally with a static server:')
console.log();
if (useYarn) {
@@ -222,8 +232,8 @@ function build(previousSizeMap) {
} else {
console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server');
}
- console.log(' ' + chalk.cyan('pushstate-server') + ' build');
- console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000');
+ console.log(' ' + chalk.cyan('pushstate-server') + ' ' + build);
+ console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:' + (process.env.PORT || 9000));
console.log();
}
});
diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js
index a8ee0fc3102..0358d4256f4 100644
--- a/packages/react-scripts/scripts/eject.js
+++ b/packages/react-scripts/scripts/eject.js
@@ -1,3 +1,4 @@
+// @remove-file-on-eject
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
@@ -7,13 +8,14 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-var createJestConfig = require('../utils/createJestConfig');
var fs = require('fs-extra');
var path = require('path');
-var paths = require('../config/paths');
-var prompt = require('react-dev-utils/prompt');
var spawnSync = require('cross-spawn').sync;
var chalk = require('chalk');
+var prompt = require('react-dev-utils/prompt');
+var paths = require('../config/paths');
+var createJestConfig = require('./utils/createJestConfig');
+
var green = chalk.green;
var cyan = chalk.cyan;
@@ -28,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))) {
@@ -45,44 +47,48 @@ prompt(
var folders = [
'config',
- path.join('config', 'jest'),
- 'scripts'
+ 'config/jest',
+ 'scripts',
+ 'scripts/utils',
];
- var files = [
- path.join('config', 'env.js'),
- path.join('config', 'paths.js'),
- path.join('config', 'polyfills.js'),
- path.join('config', 'webpack.config.dev.js'),
- path.join('config', 'webpack.config.prod.js'),
- path.join('config', 'jest', 'cssTransform.js'),
- path.join('config', 'jest', 'fileTransform.js'),
- path.join('scripts', 'build.js'),
- path.join('scripts', 'start.js'),
- path.join('scripts', 'test.js')
- ];
+ // Make shallow array of files paths
+ var files = folders.reduce(function (files, folder) {
+ return files.concat(
+ fs.readdirSync(path.join(ownPath, folder))
+ // set full path
+ .map(file => path.join(ownPath, folder, file))
+ // omit dirs from file list
+ .filter(file => fs.lstatSync(file).isFile())
+ );
+ }, []);
// Ensure that the app folder is clean and we won't override any files
folders.forEach(verifyAbsent);
files.forEach(verifyAbsent);
- // Copy the files over
+ console.log();
+ console.log(cyan('Copying files into ' + appPath));
+
folders.forEach(function(folder) {
fs.mkdirSync(path.join(appPath, folder))
});
- console.log();
- console.log(cyan('Copying files into ' + appPath));
files.forEach(function(file) {
- console.log(' Adding ' + cyan(file) + ' to the project');
- var content = fs
- .readFileSync(path.join(ownPath, file), 'utf8')
+ var content = fs.readFileSync(file, 'utf8');
+
+ // Skip flagged files
+ if (content.match(/\/\/ @remove-file-on-eject/)) {
+ return;
+ }
+ content = content
// Remove dead code from .js files on eject
.replace(/\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/mg, '')
// Remove dead code from .applescript files on eject
.replace(/-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/mg, '')
.trim() + '\n';
- fs.writeFileSync(path.join(appPath, file), content);
+ console.log(' Adding ' + cyan(file.replace(ownPath, '')) + ' to the project');
+ fs.writeFileSync(file.replace(ownPath, appPath), content);
});
console.log();
@@ -129,13 +135,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;
@@ -149,13 +154,19 @@ prompt(
);
console.log();
+ 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) {}
+
if (fs.existsSync(paths.yarnLockFile)) {
console.log(cyan('Running yarn...'));
- fs.removeSync(ownPath);
spawnSync('yarnpkg', [], {stdio: 'inherit'});
} else {
console.log(cyan('Running npm install...'));
- fs.removeSync(ownPath);
spawnSync('npm', ['install'], {stdio: 'inherit'});
}
console.log(green('Ejected successfully!'));
diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js
index 864005ecac2..33c0777ef41 100644
--- a/packages/react-scripts/scripts/init.js
+++ b/packages/react-scripts/scripts/init.js
@@ -1,3 +1,4 @@
+// @remove-file-on-eject
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
@@ -64,8 +65,6 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template
}
});
- // Run yarn or npm for react and react-dom
- // TODO: having to do two npm/yarn installs is bad, can we avoid it?
var command;
var args;
@@ -92,53 +91,65 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template
fs.unlinkSync(templateDependenciesPath);
}
- console.log('Installing react and react-dom using ' + command + '...');
- console.log();
+ // Install react and react-dom for backward compatibility with old CRA cli
+ // which doesn't install react and react-dom along with react-scripts
+ // or template is presetend (via --internal-testing-template)
+ if (!isReactInstalled(appPackage) || template) {
+ console.log('Installing react and react-dom using ' + command + '...');
+ console.log();
- var proc = spawn(command, args, {stdio: 'inherit'});
- proc.on('close', function (code) {
- if (code !== 0) {
+ var proc = spawn.sync(command, args, {stdio: 'inherit'});
+ if (proc.status !== 0) {
console.error('`' + command + ' ' + args.join(' ') + '` failed');
return;
}
+ }
- // Display the most elegant way to cd.
- // This needs to handle an undefined originalDirectory for
- // backward compatibility with old global-cli's.
- var cdpath;
- if (originalDirectory &&
- path.join(originalDirectory, appName) === appPath) {
- cdpath = appName;
- } else {
- cdpath = appPath;
- }
+ // Display the most elegant way to cd.
+ // This needs to handle an undefined originalDirectory for
+ // backward compatibility with old global-cli's.
+ var cdpath;
+ if (originalDirectory &&
+ path.join(originalDirectory, appName) === appPath) {
+ cdpath = appName;
+ } else {
+ cdpath = appPath;
+ }
+ 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('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 6943f6bb95b..9f6f645af10 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -17,21 +17,20 @@ process.env.NODE_ENV = 'development';
// /~https://github.com/motdotla/dotenv
require('dotenv').config({silent: true});
+var fs = require('fs');
var chalk = require('chalk');
-var webpack = require('webpack');
-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 WebpackDevServer = require('webpack-dev-server');
var clearConsole = require('react-dev-utils/clearConsole');
var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
-var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
var getProcessForPort = require('react-dev-utils/getProcessForPort');
var openBrowser = require('react-dev-utils/openBrowser');
var prompt = require('react-dev-utils/prompt');
-var fs = require('fs');
-var config = require('../config/webpack.config.dev');
var paths = require('../config/paths');
+var config = require('../config/webpack.config.dev');
+var devServerConfig = require('../config/webpackDevServer.config');
+var createWebpackCompiler = require('./utils/createWebpackCompiler');
+var addWebpackMiddleware = require('./utils/addWebpackMiddleware');
var useYarn = fs.existsSync(paths.yarnLockFile);
var cli = useYarn ? 'yarn' : 'npm';
@@ -43,237 +42,32 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
}
// Tools like Cloud9 rely on this.
-var DEFAULT_PORT = process.env.PORT || 3000;
-var compiler;
-var handleCompile;
-
-// You can safely remove this after ejecting.
-// We only use this block for testing of Create React App itself:
-var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
-if (isSmokeTest) {
- handleCompile = function (err, stats) {
- if (err || stats.hasErrors() || stats.hasWarnings()) {
- process.exit(1);
- } else {
- process.exit(0);
- }
- };
-}
+var DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
-function setupCompiler(host, port, protocol) {
- // "Compiler" is a low-level interface to Webpack.
- // It lets us listen to some events and provide our own custom messages.
- compiler = webpack(config, handleCompile);
-
- // "invalid" event fires when you have changed a file, and Webpack is
- // recompiling a bundle. WebpackDevServer takes care to pause serving the
- // bundle, so if you refresh, it'll wait instead of serving the old one.
- // "invalid" is short for "bundle invalidated", it doesn't imply any errors.
- compiler.plugin('invalid', function() {
- if (isInteractive) {
- clearConsole();
- }
- console.log('Compiling...');
- });
-
- var isFirstCompile = true;
-
- // "done" event fires when Webpack has finished recompiling the bundle.
- // Whether or not you have warnings or errors, you will get this event.
- compiler.plugin('done', function(stats) {
- if (isInteractive) {
- clearConsole();
- }
-
- // We have switched off the default Webpack output in WebpackDevServer
- // options so we are going to "massage" the warnings and errors and present
- // them in a readable focused way.
- var messages = formatWebpackMessages(stats.toJson({}, true));
- var isSuccessful = !messages.errors.length && !messages.warnings.length;
- var showInstructions = isSuccessful && (isInteractive || isFirstCompile);
-
- if (isSuccessful) {
- console.log(chalk.green('Compiled successfully!'));
- }
-
- if (showInstructions) {
- console.log();
- console.log('The app is running at:');
- console.log();
- console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/'));
- console.log();
- console.log('Note that the development build is not optimized.');
- console.log('To create a production build, use ' + chalk.cyan(cli + ' run build') + '.');
- console.log();
- isFirstCompile = false;
- }
+function run(port) {
+ var protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
+ var host = process.env.HOST || 'localhost';
- // If errors exist, only show errors.
- if (messages.errors.length) {
- console.log(chalk.red('Failed to compile.'));
- console.log();
- messages.errors.forEach(message => {
- console.log(message);
- console.log();
- });
+ // Create a webpack compiler that is configured with custom messages.
+ var compiler = createWebpackCompiler(config, function onReady(showInstructions) {
+ if (!showInstructions) {
return;
}
-
- // Show warnings if no errors were found.
- if (messages.warnings.length) {
- console.log(chalk.yellow('Compiled with warnings.'));
- console.log();
- messages.warnings.forEach(message => {
- console.log(message);
- console.log();
- });
- // Teach some ESLint tricks.
- console.log('You may use special comments to disable some warnings.');
- console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
- console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
- }
- });
-}
-
-// We need to provide a custom onError function for httpProxyMiddleware.
-// It allows us to log custom error messages on the console.
-function onProxyError(proxy) {
- return function(err, req, res){
- var host = req.headers && req.headers.host;
- console.log(
- chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) +
- ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.'
- );
- console.log(
- 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
- chalk.cyan(err.code) + ').'
- );
console.log();
-
- // And immediately send the proper error response to the client.
- // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
- if (res.writeHead && !res.headersSent) {
- res.writeHead(500);
- }
- res.end('Proxy error: Could not proxy request ' + req.url + ' from ' +
- host + ' to ' + proxy + ' (' + err.code + ').'
- );
- }
-}
-
-function addMiddleware(devServer) {
- // `proxy` lets you to specify a fallback server during development.
- // Every unrecognized request will be forwarded to it.
- var proxy = require(paths.appPackageJson).proxy;
- devServer.use(historyApiFallback({
- // Paths with dots should still use the history fallback.
- // See /~https://github.com/facebookincubator/create-react-app/issues/387.
- disableDotRule: true,
- // For single page apps, we generally want to fallback to /index.html.
- // However we also want to respect `proxy` for API calls.
- // So if `proxy` is specified, we need to decide which fallback to use.
- // We use a heuristic: if request `accept`s text/html, we pick /index.html.
- // Modern browsers include text/html into `accept` header when navigating.
- // However API calls like `fetch()` won’t generally accept text/html.
- // If this heuristic doesn’t work well for you, don’t use `proxy`.
- htmlAcceptHeaders: proxy ?
- ['text/html'] :
- ['text/html', '*/*']
- }));
- if (proxy) {
- if (typeof proxy !== 'string') {
- console.log(chalk.red('When specified, "proxy" in package.json must be a string.'));
- 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);
- }
-
- // Otherwise, if proxy is specified, we will let it handle any request.
- // There are a few exceptions which we won't send to the proxy:
- // - /index.html (served as HTML5 history API fallback)
- // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading)
- // - /sockjs-node/* (WebpackDevServer uses this for hot reloading)
- // Tip: use https://jex.im/regulex/ to visualize the regex
- var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
-
- // Pass the scope regex both to Express and to the middleware for proxying
- // of both HTTP and WebSockets to work without false positives.
- var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), {
- target: proxy,
- logLevel: 'silent',
- onProxyReq: function(proxyReq, req, res) {
- // 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.
- if (proxyReq.getHeader('origin')) {
- proxyReq.setHeader('origin', proxy);
- }
- },
- onError: onProxyError(proxy),
- secure: false,
- changeOrigin: true,
- ws: true
- });
- devServer.use(mayProxy, hpm);
-
- // Listen for the websocket 'upgrade' event and upgrade the connection.
- // If this is not done, httpProxyMiddleware will not try to upgrade until
- // an initial plain HTTP request is made.
- devServer.listeningApp.on('upgrade', hpm.upgrade);
- }
-
- // Finally, by now we have certainly resolved the URL.
- // It may be /index.html, so let the dev server try serving it again.
- devServer.use(devServer.middleware);
-}
-
-function runDevServer(host, port, protocol) {
- var devServer = new WebpackDevServer(compiler, {
- // Enable gzip compression of generated files.
- compress: true,
- // Silence WebpackDevServer's own logs since they're generally not useful.
- // It will still show compile warnings and errors with this setting.
- clientLogLevel: 'none',
- // By default WebpackDevServer serves physical files from current directory
- // in addition to all the virtual build products that it serves from memory.
- // This is confusing because those files won’t automatically be available in
- // production build folder unless we copy them. However, copying the whole
- // 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_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
- // for files like `favicon.ico`, `manifest.json`, and libraries that are
- // for some reason broken when imported through Webpack. If you just want to
- // use an image, put it in `src` and `import` it from JavaScript instead.
- contentBase: paths.appPublic,
- // Enable hot reloading server. It will provide /sockjs-node/ endpoint
- // for the WebpackDevServer client so it can learn when the files were
- // updated. The WebpackDevServer client is included as an entry point
- // in the Webpack development configuration. Note that only changes
- // to CSS are currently hot reloaded. JS changes will refresh the browser.
- hot: true,
- // It is important to tell WebpackDevServer to use the same "root" path
- // as we specified in the config. In development, we always serve from /.
- publicPath: config.output.publicPath,
- // WebpackDevServer is noisy by default so we emit custom message instead
- // by listening to the compiler events with `compiler.plugin` calls above.
- quiet: true,
- // Reportedly, this avoids CPU overload on some systems.
- // /~https://github.com/facebookincubator/create-react-app/issues/293
- watchOptions: {
- ignored: /node_modules/
- },
- // Enable HTTPS if the HTTPS environment variable is set to 'true'
- https: protocol === "https",
- host: host,
- overlay: false,
+ console.log('The app is running at:');
+ console.log();
+ console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/'));
+ console.log();
+ console.log('Note that the development build is not optimized.');
+ console.log('To create a production build, use ' + chalk.cyan(cli + ' run build') + '.');
+ console.log();
});
+ // Serve webpack assets generated by the compiler over a web sever.
+ var devServer = new WebpackDevServer(compiler, devServerConfig);
+
// Our custom middleware proxies requests to /index.html or a remote API.
- addMiddleware(devServer);
+ addWebpackMiddleware(devServer);
// Launch WebpackDevServer.
devServer.listen(port, (err, result) => {
@@ -291,13 +85,6 @@ function runDevServer(host, port, protocol) {
});
}
-function run(port) {
- var protocol = process.env.HTTPS === 'true' ? "https" : "http";
- var host = process.env.HOST || 'localhost';
- setupCompiler(host, port, protocol);
- runDevServer(host, port, protocol);
-}
-
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `detect()` Promise resolves to the next free port.
detect(DEFAULT_PORT).then(port => {
diff --git a/packages/react-scripts/scripts/test.js b/packages/react-scripts/scripts/test.js
index 9de5181d739..dfd2c75c728 100644
--- a/packages/react-scripts/scripts/test.js
+++ b/packages/react-scripts/scripts/test.js
@@ -28,7 +28,7 @@ if (!process.env.CI && argv.indexOf('--coverage') < 0) {
// @remove-on-eject-begin
// This is not necessary after eject because we embed config into package.json.
-const createJestConfig = require('../utils/createJestConfig');
+const createJestConfig = require('./utils/createJestConfig');
const path = require('path');
const paths = require('../config/paths');
argv.push('--config', JSON.stringify(createJestConfig(
diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js
new file mode 100644
index 00000000000..16bf40c3709
--- /dev/null
+++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js
@@ -0,0 +1,97 @@
+var chalk = require('chalk');
+var historyApiFallback = require('connect-history-api-fallback');
+var httpProxyMiddleware = require('http-proxy-middleware');
+var paths = require('../../config/paths');
+
+// We need to provide a custom onError function for httpProxyMiddleware.
+// It allows us to log custom error messages on the console.
+function onProxyError(proxy) {
+ return function(err, req, res){
+ var host = req.headers && req.headers.host;
+ console.log(
+ chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) +
+ ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.'
+ );
+ console.log(
+ 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
+ chalk.cyan(err.code) + ').'
+ );
+ console.log();
+
+ // And immediately send the proper error response to the client.
+ // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
+ if (res.writeHead && !res.headersSent) {
+ res.writeHead(500);
+ }
+ res.end('Proxy error: Could not proxy request ' + req.url + ' from ' +
+ host + ' to ' + proxy + ' (' + err.code + ').'
+ );
+ }
+}
+
+module.exports = function addWebpackMiddleware(devServer) {
+ // `proxy` lets you to specify a fallback server during development.
+ // Every unrecognized request will be forwarded to it.
+ var proxy = require(paths.appPackageJson).proxy;
+ devServer.use(historyApiFallback({
+ // Paths with dots should still use the history fallback.
+ // See /~https://github.com/facebookincubator/create-react-app/issues/387.
+ disableDotRule: true,
+ // For single page apps, we generally want to fallback to /index.html.
+ // However we also want to respect `proxy` for API calls.
+ // So if `proxy` is specified, we need to decide which fallback to use.
+ // We use a heuristic: if request `accept`s text/html, we pick /index.html.
+ // Modern browsers include text/html into `accept` header when navigating.
+ // However API calls like `fetch()` won’t generally accept text/html.
+ // If this heuristic doesn’t work well for you, don’t use `proxy`.
+ htmlAcceptHeaders: proxy ?
+ ['text/html'] :
+ ['text/html', '*/*']
+ }));
+ if (proxy) {
+ if (typeof proxy !== 'string') {
+ console.log(chalk.red('When specified, "proxy" in package.json must be a string.'));
+ 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);
+ }
+
+ // Otherwise, if proxy is specified, we will let it handle any request.
+ // There are a few exceptions which we won't send to the proxy:
+ // - /index.html (served as HTML5 history API fallback)
+ // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading)
+ // - /sockjs-node/* (WebpackDevServer uses this for hot reloading)
+ // Tip: use https://jex.im/regulex/ to visualize the regex
+ var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
+
+ // Pass the scope regex both to Express and to the middleware for proxying
+ // of both HTTP and WebSockets to work without false positives.
+ var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), {
+ target: proxy,
+ logLevel: 'silent',
+ onProxyReq: function(proxyReq, req, res) {
+ // 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.
+ if (proxyReq.getHeader('origin')) {
+ proxyReq.setHeader('origin', proxy);
+ }
+ },
+ onError: onProxyError(proxy),
+ secure: false,
+ changeOrigin: true,
+ ws: true,
+ xfwd: true
+ });
+ devServer.use(mayProxy, hpm);
+
+ // Listen for the websocket 'upgrade' event and upgrade the connection.
+ // If this is not done, httpProxyMiddleware will not try to upgrade until
+ // an initial plain HTTP request is made.
+ devServer.listeningApp.on('upgrade', hpm.upgrade);
+ }
+
+ // Finally, by now we have certainly resolved the URL.
+ // It may be /index.html, so let the dev server try serving it again.
+ devServer.use(devServer.middleware);
+};
diff --git a/packages/react-scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js
similarity index 94%
rename from packages/react-scripts/utils/createJestConfig.js
rename to packages/react-scripts/scripts/utils/createJestConfig.js
index f1c67c018f1..c99345b858a 100644
--- a/packages/react-scripts/utils/createJestConfig.js
+++ b/packages/react-scripts/scripts/utils/createJestConfig.js
@@ -1,3 +1,4 @@
+// @remove-file-on-eject
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
@@ -7,10 +8,8 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-// Note: this file does not exist after ejecting.
-
const fs = require('fs');
-const paths = require('../config/paths');
+const paths = require('../../config/paths');
module.exports = (resolve, rootDir, isEjecting) => {
// Use this instead of `paths.testsSetup` to avoid putting
diff --git a/packages/react-scripts/scripts/utils/createWebpackCompiler.js b/packages/react-scripts/scripts/utils/createWebpackCompiler.js
new file mode 100644
index 00000000000..932bfd54034
--- /dev/null
+++ b/packages/react-scripts/scripts/utils/createWebpackCompiler.js
@@ -0,0 +1,98 @@
+var chalk = require('chalk');
+var webpack = require('webpack');
+var clearConsole = require('react-dev-utils/clearConsole');
+var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
+
+var isInteractive = process.stdout.isTTY;
+var handleCompile;
+
+// You can safely remove this after ejecting.
+// We only use this block for testing of Create React App itself:
+var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1);
+if (isSmokeTest) {
+ handleCompile = function (err, stats) {
+ if (err || stats.hasErrors() || stats.hasWarnings()) {
+ process.exit(1);
+ } else {
+ process.exit(0);
+ }
+ };
+}
+
+module.exports = function createWebpackCompiler(config, onReadyCallback) {
+ // "Compiler" is a low-level interface to Webpack.
+ // It lets us listen to some events and provide our own custom messages.
+ try {
+ var compiler = webpack(config, handleCompile);
+ } catch (err) {
+ console.log(chalk.red('Failed to compile.'));
+ console.log();
+ console.log(err.message || err);
+ console.log();
+ process.exit(1);
+ }
+
+ // "invalid" event fires when you have changed a file, and Webpack is
+ // recompiling a bundle. WebpackDevServer takes care to pause serving the
+ // bundle, so if you refresh, it'll wait instead of serving the old one.
+ // "invalid" is short for "bundle invalidated", it doesn't imply any errors.
+ compiler.plugin('invalid', function() {
+ if (isInteractive) {
+ clearConsole();
+ }
+ console.log('Compiling...');
+ });
+
+ var isFirstCompile = true;
+
+ // "done" event fires when Webpack has finished recompiling the bundle.
+ // Whether or not you have warnings or errors, you will get this event.
+ compiler.plugin('done', function(stats) {
+ if (isInteractive) {
+ clearConsole();
+ }
+
+ // We have switched off the default Webpack output in WebpackDevServer
+ // options so we are going to "massage" the warnings and errors and present
+ // them in a readable focused way.
+ var messages = formatWebpackMessages(stats.toJson({}, true));
+ var isSuccessful = !messages.errors.length && !messages.warnings.length;
+ var showInstructions = isSuccessful && (isInteractive || isFirstCompile);
+
+ if (isSuccessful) {
+ console.log(chalk.green('Compiled successfully!'));
+ }
+
+ if (typeof onReadyCallback === 'function') {
+ onReadyCallback(showInstructions);
+ }
+ isFirstCompile = false;
+
+ // If errors exist, only show errors.
+ if (messages.errors.length) {
+ console.log(chalk.red('Failed to compile.'));
+ console.log();
+ messages.errors.forEach(message => {
+ console.log(message);
+ console.log();
+ });
+ return;
+ }
+
+ // Show warnings if no errors were found.
+ if (messages.warnings.length) {
+ console.log(chalk.yellow('Compiled with warnings.'));
+ console.log();
+ messages.warnings.forEach(message => {
+ console.log(message);
+ console.log();
+ });
+ // Teach some ESLint tricks.
+ console.log('You may use special comments to disable some warnings.');
+ console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.');
+ console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.');
+ }
+ });
+
+ return compiler;
+};
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index c49da3e8d34..db06a0e0e5a 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,6 +30,7 @@ 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)
@@ -39,6 +43,7 @@ You can find the most recent version of this guide [here](/~https://github.com/fac
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
- [Using HTTPS in Development](#using-https-in-development)
- [Generating Dynamic `` 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)
@@ -59,6 +64,7 @@ You can find the most recent version of this guide [here](/~https://github.com/fac
- [Deployment](#deployment)
- [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)
@@ -170,6 +176,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.
+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.
@@ -216,20 +245,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 ``
You can find the source HTML file in the `public` folder of the generated project. You may edit the `` tag in it to change the title from “React App” to anything else.
-Note that normally you wouldn't edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML.
+Note that normally you wouldn’t edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML.
If you need to dynamically update the page title based on the content, you can use the browser [`document.title`](https://developer.mozilla.org/en-US/docs/Web/API/Document/title) API. For more complex scenarios when you want to change the title from React components, you can use [React Helmet](/~https://github.com/nfl/react-helmet), a third party library.
-Finally, if you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server).
+If you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server). Alternatively, you can pre-build each page as a static HTML file which then loads the JavaScript bundle, which is covered [here](#pre-rendering-into-static-html-files).
## Installing a Dependency
@@ -351,7 +409,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 `` and `` components, we recommend creating a `