diff --git a/.gitignore b/.gitignore index 1900a2f2cf9..7444dad4ec2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ my-app* template/src/__tests__/__snapshots__/ lerna-debug.log npm-debug.log +/.changelog diff --git a/.travis.yml b/.travis.yml index 75383b87806..bb9c8112081 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ --- language: node_js node_js: + - 0.10 - 4 - 6 cache: @@ -9,3 +10,6 @@ cache: - packages/create-react-app/node_modules - packages/react-scripts/node_modules script: tasks/e2e.sh +env: + - USE_YARN=no + - USE_YARN=yes diff --git a/CHANGELOG.md b/CHANGELOG.md index a37b867c6a9..113bf641366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,481 @@ +## 0.8.5 (January 9, 2017) + +#### :bug: Bug Fix +* `create-react-app`, `react-scripts` + * [#1365](https://github.com/facebookincubator/create-react-app/pull/1365) Use yarnpkg alias to run Yarn. ([@fson](https://github.com/fson)) + + Fixes an issue where running `create-react-app` failed on systems with Apache Hadoop installed because it falsely detected Hadoop YARN executable as Yarn package manager. + +#### Committers: 1 +- Ville Immonen ([fson](https://github.com/fson)) + +### Migrating from 0.8.4 to 0.8.5 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.5 +``` + +You may also optionally update the global command-line utility: + +``` +npm install -g create-react-app@1.0.3 +``` + +## 0.8.4 (December 11, 2016) + +#### :bug: Bug Fix +* `react-scripts` + + * [#1233](https://github.com/facebookincubator/create-react-app/pull/1233) Disable subresource integrity temporarily. ([@Timer](https://github.com/Timer)) + + We added [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) checks to the build output in 0.8.2 but it turns out that they may fail in browsers using special compression proxies, such as Chrome on Android, when served over HTTP. We disabled the checks until we can find a safe way to add them. + +* `react-dev-utils` + + * [#1226](https://github.com/facebookincubator/create-react-app/pull/1226) Fix weird lint output. ([@n3tr](https://github.com/n3tr)) + + Fixes strange lint message formatting in some edge cases. + + * [#1215](https://github.com/facebookincubator/create-react-app/pull/1215) Fix - openChrome won't open default browser (using Canary). ([@n3tr](https://github.com/n3tr)) + + Fixes a regression that caused stable Google Chrome to be opened even if you are using Canary as the default browser. + +* `create-react-app` + + * [#1223](https://github.com/facebookincubator/create-react-app/pull/1223) Clean up Yarn detection and install code. ([@fson](https://github.com/fson)) + + Fixes noisy output on Windows when Yarn is not installed. + + * [#1224](https://github.com/facebookincubator/create-react-app/pull/1224) Exit with an error code when npm/yarn install fails. ([@fson](https://github.com/fson)) + +#### :nail_care: Enhancement +* `react-scripts` + + * [#1237](https://github.com/facebookincubator/create-react-app/pull/1237) Clear scrollback in test mode. ([@gaearon](https://github.com/gaearon)) + + Ensures test watcher clears the console before running. + + * [#1229](https://github.com/facebookincubator/create-react-app/pull/1229) Disable jest watch mode when --coverage flag is present [#1207]. ([@BenoitAverty](https://github.com/BenoitAverty)) + + Since coverage doesn't work well with watch mode, we don’t run the watcher on `npm test -- --coverage` anymore. + + * [#1212](https://github.com/facebookincubator/create-react-app/pull/1212) Proxy rewrites Origin header to match the target server URL. ([@koles](https://github.com/koles)) + + Makes sure more API endpoints can work with the `proxy` setting. + + * [#1222](https://github.com/facebookincubator/create-react-app/pull/1222) Disable gh-page setup instruction if scripts.deploy has been added. ([@n3tr](https://github.com/n3tr)) + + Suppresses the instructions printed at the end of `npm run build` if `npm run deploy` already exists. + +* `create-react-app` + + * [#1236](https://github.com/facebookincubator/create-react-app/pull/1236) Tweak console messages. ([@gaearon](https://github.com/gaearon)) + + Makes error messages more friendly. + + * [#1195](https://github.com/facebookincubator/create-react-app/pull/1195) Use "commander" for cli argv handling. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + + Adds `create-react-app --help` with a list of options. + +* `react-dev-utils` + + * [#1211](https://github.com/facebookincubator/create-react-app/pull/1211) Use a better clear console sequence. ([@gaearon](https://github.com/gaearon)) + + Ensures the development server clears the terminal when files are changed. + +#### :memo: Documentation +* `react-dev-utils` + + * [#1232](https://github.com/facebookincubator/create-react-app/pull/1232) [documentation] fix html-dev-plugin link in react-dev-utils doc. ([@shogunsea](https://github.com/shogunsea)) + +* `react-scripts` + + * [#1220](https://github.com/facebookincubator/create-react-app/pull/1220) Adding troubleshooting information about Subresource Integrity digests.. ([@dfbaskin](https://github.com/dfbaskin)) + +#### :house: Internal +* `react-scripts` + + * [#1214](https://github.com/facebookincubator/create-react-app/pull/1214) Bump babel-eslint version. ([@existentialism](https://github.com/existentialism)) + +#### Committers: 10 +- Benoit Averty ([BenoitAverty](https://github.com/BenoitAverty)) +- Brian Ng ([existentialism](https://github.com/existentialism)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Dave Baskin ([dfbaskin](https://github.com/dfbaskin)) +- Fabrizio Castellarin ([EnoahNetzach](https://github.com/EnoahNetzach)) +- Jirat Ki. ([n3tr](https://github.com/n3tr)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Pavel Kolesnikov ([koles](https://github.com/koles)) +- Shogun Sea ([shogunsea](https://github.com/shogunsea)) +- Ville Immonen ([fson](https://github.com/fson)) + +### Migrating from 0.8.3 to 0.8.4 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.4 +``` + +You may also optionally update the global command-line utility: + +``` +npm install -g create-react-app@1.0.2 +``` + +## 0.8.3 (December 8, 2016) + +#### :bug: Bug Fix +* `create-react-app` + * [#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 +* `react-scripts` + + * [#806](https://github.com/facebookincubator/create-react-app/pull/806) Add syntax highlighting configuration guide. ([@mareksuscak](https://github.com/mareksuscak)) + +#### Committers: 3 +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Marek Suscak ([mareksuscak](https://github.com/mareksuscak)) +- Remon Georgy ([remon-georgy](https://github.com/remon-georgy)) + +### Migrating from 0.8.2 to 0.8.3 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.3 +``` + +You can optionally update the global CLI too: + +``` +npm install -g create-react-app@1.0.1 +``` + +## 0.8.2 (December 7, 2016) + +#### :rocket: New Feature +* `react-scripts` + * [#1176](https://github.com/facebookincubator/create-react-app/pull/1176) Add Subresource Integrity support. ([@XVincentX](https://github.com/XVincentX)) + + The generated HTML now includes [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) attributes ensuring that your users aren't served malicious code if your CDN gets compromised. + +#### :bug: Bug Fix +* `react-scripts` + * [#1197](https://github.com/facebookincubator/create-react-app/pull/1197) Let Jest handle all file types. ([@gaearon](https://github.com/gaearon)) + + Since 0.8.0, we started treating imports of any unknown file extensions as URLs. However, we had to revert this change for the test configuration in 0.8.1 because of a bug causing false positives. In 0.8.2, we are fixing this and making test configuration treat imports with unknown extensions the same way as we do in the browser environment. + + * [#1194](https://github.com/facebookincubator/create-react-app/pull/1194) Only honor relative `NODE_PATH`. ([@gaearon](https://github.com/gaearon)) + + Historically we have allowed specifying `NODE_PATH` environment variable as a way to allow “absolute imports”. For example, running `NODE_PATH=src npm start` in Bash or `set NODE_PATH=src&&npm start` in Windows Cmd would let you import anything inside `src` without specifying a relative path. However, we found a few nasty edge cases when Node.js core modules end up being in `NODE_PATH` and erroneously become bundled. As a result the build would crash on some systems when some libraries are imported. To fix this, we now only honor relative paths from `NODE_PATH` in Create React App. This means the existing use case for absolute imports is still supported (`src` in the example above is relative), but absolute paths in `NODE_PATH` (such as paths to Node.js core modules) will be ignored. + + * [#1188](https://github.com/facebookincubator/create-react-app/pull/1188) Update Webpack to fix source map issues. ([@gaearon](https://github.com/gaearon)) + + Since 0.8.0, we show source maps in development instead of the compiled code. However, it has come to our attention that Webpack's source map implementation had issues interpreting Babel output, and caused source maps to be wrong and breakpoints to be unusable in some cases. Webpack has released a fix for this, and we have updated the minimal version of Webpack that we are using. + + * [#1180](https://github.com/facebookincubator/create-react-app/pull/1180) Use `file-loader` for svgs. ([@bogdansoare](https://github.com/bogdansoare)) + + Since 0.8.0, we are treating all imports with non-JS/CSS extensions the same way. Importing them gives you a string with their URL, and if their content is small enough (less than 10K), the URL is in fact an inlined [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs). However, this doesn't work well with SVGs in case you use them for a sprite system since fragments don't work in data URIs, and it's wasteful to inline the same sprite SVG many times. To fix this, we have added an exception so that SVG files never get inlined. + +* `react-dev-utils` + * [#1165](https://github.com/facebookincubator/create-react-app/pull/1165) Chrome 'open tab' reuse an empty tab when possible. ([@n3tr](https://github.com/n3tr)) + + Fixes an issue that caused two tabs to get opened instead of just one. It also fixes some cases where the window with the existing tab would not get activated. + +* `babel-preset-react-app` + * [#1179](https://github.com/facebookincubator/create-react-app/pull/1179) Fix Babel issues in tests by applying the right transforms. ([@gaearon](https://github.com/gaearon)) + + Fixes regressions in test environment that caused syntax errors with generators and `async` / `await`. + +#### :nail_care: Enhancement +* `eslint-config-react-app` + * [#1191](https://github.com/facebookincubator/create-react-app/pull/1191) Relax peerDependencies for ESLint preset. ([@gaearon](https://github.com/gaearon)) + + This allows the preset to be used in more apps without peer dependency conflicts. We still pin the exact versions in apps that haven't ejected for extra safety. + + * [#1159](https://github.com/facebookincubator/create-react-app/pull/1159) Make jsx-no-undef rule an error. ([@existentialism](https://github.com/existentialism)) + + Using an undefined type in JSX is now treated as a hard lint error because it is guaranteed to crash application at runtime. + +* `react-scripts` + * [#1175](https://github.com/facebookincubator/create-react-app/pull/1175) Remove path module from webpack config on eject. ([@harunhasdal](https://github.com/harunhasdal)) + + This makes the output after ejecting a bit cleaner. + + * [#1120](https://github.com/facebookincubator/create-react-app/pull/1120) Add `testURL` to Jest config. ([@spudly](https://github.com/spudly)) + + This fixes an error when running tests that interact with History API in jsdom. + +#### :memo: Documentation +* `react-scripts` + * [#1143](https://github.com/facebookincubator/create-react-app/pull/1143) Add deploy to Firebase CDN on template's README (Closes [#374](https://github.com/facebookincubator/create-react-app/issues/374)). ([@guilhermebruzzi](https://github.com/guilhermebruzzi)) + * [#1099](https://github.com/facebookincubator/create-react-app/pull/1099) Fix minor typo/grammar. ([@alex-wilmer](https://github.com/alex-wilmer)) + * [#1168](https://github.com/facebookincubator/create-react-app/pull/1168) Add "npm run build silently fails" to Troubleshooting. ([@gaearon](https://github.com/gaearon)) + +#### Committers: 12 +- Alex Wilmer ([alex-wilmer](https://github.com/alex-wilmer)) +- Bogdan Soare ([bogdansoare](https://github.com/bogdansoare)) +- Brian Ng ([existentialism](https://github.com/existentialism)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Fabrizio Castellarin ([EnoahNetzach](https://github.com/EnoahNetzach)) +- Guilherme Heynemann Bruzzi ([guilhermebruzzi](https://github.com/guilhermebruzzi)) +- Harun ([harunhasdal](https://github.com/harunhasdal)) +- James Newell ([jameslnewell](https://github.com/jameslnewell)) +- Jirat Ki. ([n3tr](https://github.com/n3tr)) +- Li Xuanji ([zodiac](https://github.com/zodiac)) +- Stephen John Sorensen ([spudly](https://github.com/spudly)) +- Vincenzo Chianese ([XVincentX](https://github.com/XVincentX)) + +### Migrating from 0.8.1 to 0.8.2 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.2 +``` + +## 0.8.1 (December 4, 2016) + +#### :bug: Bug Fix +* `react-scripts` + * [#1149](https://github.com/facebookincubator/create-react-app/pull/1149) Fix incorrectly stubbing JavaScript files with a dot in the import path in tests. ([@fson](https://github.com/fson)) + +### Migrating from 0.8.0 to 0.8.1 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.1 +``` + +## 0.8.0 (December 3, 2016) + +#### :rocket: New Feature +* `react-scripts` + * [#944](https://github.com/facebookincubator/create-react-app/pull/944) Crash the build during CI whenever linter warnings are encountered. ([@excitement-engineer](https://github.com/excitement-engineer)) + + Linter warnings and errors are now checked during a continuous integration build (set by the `CI` environment variable) and the build will fail if any issues are found. See [Continuous Integration](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#continuous-integration) for more information. + + * [#1090](https://github.com/facebookincubator/create-react-app/pull/1090) Enable proxying of WebSockets. ([@dceddia](https://github.com/dceddia)) + +* `create-react-app`, `react-scripts` + * [#898](https://github.com/facebookincubator/create-react-app/pull/898) Support Yarn. ([@fson](https://github.com/fson)) + + Yarn is a new fast, reliable and secure alternative to the `npm` client. If you have Yarn installed, `create-react-app` will use it to install packages when you create an app. It also creates a `yarn.lock` file that should be checked into source control (e.g. git). This ensures the same versions of packages will be installed each time `yarn install` is run, on any machine. + + `react-scripts` now also displays instructions using `yarn` commands for projects using Yarn (projects having a `yarn.lock` file). + + To create a project using Yarn, simply install `yarn` and use `create-react-app` like before: + ``` + npm install -g yarn create-react-app@latest + + create-react-app my-app # Packages are now installed with Yarn. + ``` + +#### :boom: Breaking Change +* `babel-preset-react-app` + * [#902](https://github.com/facebookincubator/create-react-app/pull/902) Enable useBuiltIns option on object-rest-spread. ([@existentialism](https://github.com/existentialism)) + + Object rest spread and JSX now use the native `Object.assign()` method instead of Babel's helper function. If you are using `babel-preset-react-app` directly in your project *and* targeting browsers that don't have `Object.assign()` available, from now on you need a polyfill for it (e.g. [`object-assign`](https://www.npmjs.com/package/object-assign)). + + **Note:** `react-scripts` already adds this polyfill, so no changes are necessary in Create React App projects. + +#### :bug: Bug Fix +* `react-scripts` + * [#978](https://github.com/facebookincubator/create-react-app/pull/978) Move the remove-on-eject-end tag at the end of the file. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + + Fixes a bug in ejected configuration. + + * [#1017](https://github.com/facebookincubator/create-react-app/pull/1017) Don't look for `.babelrc` file during test. ([@nhajidin](https://github.com/nhajidin)) + + Fixes a `.babelrc` file in a parent directory interfering with the `npm test` command. + + * [#951](https://github.com/facebookincubator/create-react-app/pull/951) Check for presence of folders before continuing eject. ([@heldinz](https://github.com/heldinz)) + + Fixes a bug where `eject` failed when a `scripts` or `config` folder already existed in the project. + +* `react-dev-utils` + * [#1035](https://github.com/facebookincubator/create-react-app/pull/1035) Fix Chrome tab reuse. ([@einarlove](https://github.com/einarlove)) + + Fixes a bug with the app not opening in the existing tab in Chrome. + + * [#964](https://github.com/facebookincubator/create-react-app/pull/964) Catch and noop call to open web browser. ([@spadin](https://github.com/spadin)) + + Not being able to open a browser doesn't crash the development server now. + +* `eslint-config-react-app`, `react-scripts` + * [#953](https://github.com/facebookincubator/create-react-app/pull/953) Fix `.ico` file extension being handled by test configuration. ([@vadzim](https://github.com/vadzim)) + +#### :nail_care: Enhancement +* `react-scripts` + * [#1032](https://github.com/facebookincubator/create-react-app/pull/1032) Add support for non-interactive terminal. ([@sheerun](https://github.com/sheerun)) + * [#1078](https://github.com/facebookincubator/create-react-app/pull/1078) Upgrade Jest to 17.0. ([@fson](https://github.com/fson)) + * [#1059](https://github.com/facebookincubator/create-react-app/pull/1059) Use `url-loader` with limit 10k as a default loader. ([@bebbi](https://github.com/bebbi)) + + `react-scripts` now treats imports with any unknown file extension as a resource. Files with a size below 10 KB are inlined using a data URI and larger files copied to the build folder. This removes the need for an internal [whitelist of supported file extensions](https://github.com/facebookincubator/create-react-app/issues/667). Any file that's not JS or CSS is now handled the same way. + + * [#924](https://github.com/facebookincubator/create-react-app/pull/924) Enable JavaScript source maps in development. ([@ekaradon](https://github.com/ekaradon)) + * [#1058](https://github.com/facebookincubator/create-react-app/pull/1058) Add missing dev argument in build script message. ([@nhajidin](https://github.com/nhajidin)) + * [#961](https://github.com/facebookincubator/create-react-app/pull/961) Add `collectCoverageFrom` option to collect coverage on files without any tests. ([@pmackcode](https://github.com/pmackcode)) + + The test script now considers all files in the project when calculating test coverage. + + * [#968](https://github.com/facebookincubator/create-react-app/pull/968) Enable gzip compression in the development server (#966). ([@frontsideair](https://github.com/frontsideair)) +* `react-dev-utils`, `react-scripts` + * [#816](https://github.com/facebookincubator/create-react-app/pull/816) add logging of existing default port process on start. ([@ianmcnally](https://github.com/ianmcnally)) + + `react-scripts` can guess which process is running on the port 3000 when it's not available: + ``` + Something is already running on port 3000. Probably: + my-app + in /Users/ian/dev/my-app + + Would you like to run the app on another port instead? + ``` +* `react-dev-utils` + * [#963](https://github.com/facebookincubator/create-react-app/pull/963) Allow webpack 2 as a peerDependency in react-dev-utils. ([@einarlove](https://github.com/einarlove)) + +#### :memo: Documentation +* `react-scripts` + * [#1126](https://github.com/facebookincubator/create-react-app/pull/1126) Add a note about vscode-jest. ([@orta](https://github.com/orta)) + * [#1080](https://github.com/facebookincubator/create-react-app/pull/1080) Add a note for OSX users about watchman and jest. ([@dmr](https://github.com/dmr)) + * [#1071](https://github.com/facebookincubator/create-react-app/pull/1071) Adds to docs - deployment with S3/CloudFront. ([@marcgarreau](https://github.com/marcgarreau)) + * [#976](https://github.com/facebookincubator/create-react-app/pull/976) Added info on using global variables. ([@jhorneman](https://github.com/jhorneman)) + * [#996](https://github.com/facebookincubator/create-react-app/pull/996) Remove redundant `function` from export statement. ([@gnowoel](https://github.com/gnowoel)) + * [#959](https://github.com/facebookincubator/create-react-app/pull/959) Always build before deploying to gh-pages. ([@dsernst](https://github.com/dsernst)) + * [#974](https://github.com/facebookincubator/create-react-app/pull/974) Gently nudge users towards https by default. ([@Swizec](https://github.com/Swizec)) +* Other + * [#1031](https://github.com/facebookincubator/create-react-app/pull/1031) No Configuration -> Convention over Configuration. ([@sheerun](https://github.com/sheerun)) + * [#995](https://github.com/facebookincubator/create-react-app/pull/995) Add Gatsby to alternatives. ([@KyleAMathews](https://github.com/KyleAMathews)) + +#### :house: Internal +* `react-scripts` + * [#1072](https://github.com/facebookincubator/create-react-app/pull/1072) Replace rimraf with fs-extra functions. ([@existentialism](https://github.com/existentialism)) + * [#1068](https://github.com/facebookincubator/create-react-app/pull/1068) Remove bundledDependencies. ([@fson](https://github.com/fson)) + * [#1057](https://github.com/facebookincubator/create-react-app/pull/1057) Update `css-loader`. ([@nhajidin](https://github.com/nhajidin)) + * [#983](https://github.com/facebookincubator/create-react-app/pull/983) Remove custom babel-loader cache dir config. ([@fson](https://github.com/fson)) +* `babel-preset-react-app` + * [#1052](https://github.com/facebookincubator/create-react-app/pull/1052) Remove unnecessary transform plugins for object spread to work. ([@valscion](https://github.com/valscion)) + * [#992](https://github.com/facebookincubator/create-react-app/pull/992) Explain the usage of react-jsx-source & react-jsx-self. ([@bboysathish](https://github.com/bboysathish)) + * [#1051](https://github.com/facebookincubator/create-react-app/pull/1051) Update babel-present-env and use node: 'current' as target. ([@valscion](https://github.com/valscion)) + +#### Committers: 27 +- Adam Stankiewicz ([sheerun](https://github.com/sheerun)) +- Alice Rose ([heldinz](https://github.com/heldinz)) +- Arunoda Susiripala ([arunoda](https://github.com/arunoda)) +- Brian Ng ([existentialism](https://github.com/existentialism)) +- Daniel Rech ([dmr](https://github.com/dmr)) +- Dave Ceddia ([dceddia](https://github.com/dceddia)) +- David Ernst ([dsernst](https://github.com/dsernst)) +- Dirk-Jan Rutten ([excitement-engineer](https://github.com/excitement-engineer)) +- Einar Löve ([einarlove](https://github.com/einarlove)) +- Fabrizio Castellarin ([EnoahNetzach](https://github.com/EnoahNetzach)) +- Fatih ([frontsideair](https://github.com/frontsideair)) +- Ian McNally ([ianmcnally](https://github.com/ianmcnally)) +- Jurie Horneman ([jhorneman](https://github.com/jhorneman)) +- Kyle Mathews ([KyleAMathews](https://github.com/KyleAMathews)) +- Leo Wong ([gnowoel](https://github.com/gnowoel)) +- Marc Garreau ([marcgarreau](https://github.com/marcgarreau)) +- Nazim Hajidin ([nhajidin](https://github.com/nhajidin)) +- Orta ([orta](https://github.com/orta)) +- Patrick Mackinder ([pmackcode](https://github.com/pmackcode)) +- Sandro Padin ([spadin](https://github.com/spadin)) +- Sathish ([bboysathish](https://github.com/bboysathish)) +- Stefan ([bebbi](https://github.com/bebbi)) +- Swizec Teller ([Swizec](https://github.com/Swizec)) +- Vadzim ([vadzim](https://github.com/vadzim)) +- Vesa Laakso ([valscion](https://github.com/valscion)) +- Ville Immonen ([fson](https://github.com/fson)) +- [ekaradon](https://github.com/ekaradon) + +### Migrating from 0.7.0 to 0.8.0 + +You may optionally update the global command (it’s not required, but it adds Yarn support for new projects): + +``` +npm install -g create-react-app@1.0.0 +``` + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.8.0 +``` + +## 0.7.0 (October 22, 2016) + +### Build Dependency (`react-scripts`) + +* Updates Jest to [version 16.0](http://facebook.github.io/jest/blog/2016/10/03/jest-16.html), with an upgraded CLI, improved snapshot testing, new matchers and more. ([@chase](https://github.com/chase) in [#858](https://github.com/facebookincubator/create-react-app/pull/858)) +* Test setup file `src/setupTests.js` is now called after test framework initialization to support loading custom matchers. ([@just-boris](https://github.com/just-boris) in [#846](https://github.com/facebookincubator/create-react-app/pull/846)) +* Build command shows better instructions for deploying the app to GitHub Pages ([@Janpot](https://github.com/Janpot) in [#841](https://github.com/facebookincubator/create-react-app/pull/841)) +* Build command now generates an asset manifest with mappings from each filename to its final output filename. ([@lukyth](https://github.com/lukyth) in [#891](https://github.com/facebookincubator/create-react-app/pull/891)) +* Build command exits, if there are errors from UglifyJS ([@pdillon](https://github.com/pdillon) in [#859](https://github.com/facebookincubator/create-react-app/pull/859)) +* Eject output is more beautiful now. ([@azakordonets](https://github.com/azakordonets) in [#769](https://github.com/facebookincubator/create-react-app/pull/769)) +* Fixes opening the app in a new tab in Chrome. ([@unixdev](https://github.com/unixdev) in [#831](https://github.com/facebookincubator/create-react-app/pull/831)) +* Fixes environment variables not being defined as normal properties of the `process.env` object. ([@dvkndn](https://github.com/dvkndn) in [#807](https://github.com/facebookincubator/create-react-app/pull/807)) +* Fixes PostCSS autoprefixer not processing CSS files imported with CSS `@import` statements. ([@nhunzaker](https://github.com/nhunzaker) in [#929](https://github.com/facebookincubator/create-react-app/pull/929)) + +### ESLint Config (`eslint-config-react-app`) + +* Adds `import/no-webpack-loader-syntax` rule that forbids using custom Webpack specific syntax to specify Webpack loaders in import statements. ([@fson](https://github.com/fson) in [#803](https://github.com/facebookincubator/create-react-app/pull/803)) +* `react/react-in-jsx-scope` rule ("React must be in scope") is now an error. ([@gaearon](https://github.com/gaearon) in [#822](https://github.com/facebookincubator/create-react-app/pull/822)) +* `no-unused-expressions` rule now allows the use of short circuit and ternary expressions. ([@cannona](https://github.com/cannona) in [#724](https://github.com/facebookincubator/create-react-app/pull/724)) + +### Babel Preset (`babel-preset-react-app`) + +* The preset now detects the Node.js version in test environment and disables unnecessary ES2015 transforms using using `babel-preset-env`. ([@shubheksha](https://github.com/shubheksha) in [#878](https://github.com/facebookincubator/create-react-app/pull/878), [@JeffreyATW](https://github.com/JeffreyATW) in [#927 +](https://github.com/facebookincubator/create-react-app/pull/927)) +* Fixes a duplicate dependency on `babel-plugin-transform-regenerator`. ([@akofman](https://github.com/akofman) in [#864](https://github.com/facebookincubator/create-react-app/pull/864)) + +### Utilities (`react-dev-utils`) + +* The error overlay is now disposed after fixing linting errors. ([@jarlef](https://github.com/jarlef) in [#856](https://github.com/facebookincubator/create-react-app/pull/856)) +* Adds support for Webpack 2 to `webpackHotDevClient`. ([@michalkvasnicak](https://github.com/michalkvasnicak) in [#840](https://github.com/facebookincubator/create-react-app/pull/840)) + +### Global CLI (`create-react-app`) + +* Adds support for passing a scoped package name to the `--scripts-version` argument. ([@pdillon](https://github.com/pdillon) in [#826](https://github.com/facebookincubator/create-react-app/pull/826)) +* Fixes installing pre-release versions using a tarball URL with the `--scripts-version` argument. ([@jihchi](https://github.com/jihchi) in [#876](https://github.com/facebookincubator/create-react-app/pull/876)) + +### Migrating from 0.6.1 to 0.7.0 + +You may optionally update the global command (it’s not required): + +``` +npm install -g create-react-app@0.6.0 +``` + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.7.0 +``` + +### Breaking Change in 0.7.0 + +#### Updating Snapshots + +Jest 16 includes [improvements to snapshot testing and changes to the snapshot format](https://facebook.github.io/jest/blog/2016/10/03/jest-16.html#snapshot-updates). If your project uses snapshot testing, you'll need to update the snapshot files. To update the snapshots, run: +``` +npm test -- -u +``` + ## 0.6.1 (September 27, 2016) ### Build Dependency (`react-scripts`) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc0f7aa0030..74785fd16ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,15 +60,18 @@ and then run `npm start` or `npm run build`. ## Cutting a Release -1. Tag all merged PRs that go into the release with the relevant milestone. +1. Tag all merged pull requests that go into the release with the relevant milestone. Each merged PR should also be labeled with one of the [labels](https://github.com/facebookincubator/create-react-app/labels) named `tag: ...` to indicate what kind of change it is. 2. Close the milestone. 3. In most releases, only `react-scripts` needs to be released. If you don’t have any changes to the `packages/create-react-app` folder, you don’t need to bump its version or publish it (the publish script will publish only changed packages). 4. Note that files in `packages/create-react-app` should be modified with extreme caution. Since it’s a global CLI, any version of `create-react-app` (global CLI) including very old ones should work with the latest version of `react-scripts`. -5. Add an entry to `CHANGELOG.md` detailing what has changed with links to PRs and their authors. Use previous entries for inspiration. Group changes to `react-scripts` and `create-react-app` separately in the notes, for example like in `0.2.0` release notes. +5. Create a change log entry for the release: + * You'll need an [access token for the GitHub API](https://help.github.com/articles/creating-an-access-token-for-command-line-use/). Save it to this environment variable: `export GITHUB_AUTH="..."` + * Run `npm run changelog`. The command will find all the labeled pull requests merged since the last release and group them by the label and affected packages, and create a change log entry with all the changes and links to PRs and their authors. Copy and paste it to `CHANGELOG.md`. + * Add a four-space indented paragraph after each non-trivial list item, explaining what changed and why. For each breaking change also write who it affects and instructions for migrating existing code. 6. Make sure to include “Migrating from ...” instructions for the previous release. Often you can copy and paste them. 7. After merging the changelog update, create a GitHub Release with the same text. See previous Releases for inspiration. 8. **Do not run `npm publish`. Instead, run `npm run publish`.** -9. Wait for a long time, and it will get published. Don’t worry that it’s stuck. It will bundle dependencies into a single tarball before publishing for faster installs. In the end the publish script will prompt for versions before publishing the packages. +9. Wait for a long time, and it will get published. Don’t worry that it’s stuck. In the end the publish script will prompt for versions before publishing the packages. Make sure to test the released version! If you want to be extra careful, you can publish a prerelease by running `npm run publish -- --tag next` instead of `npm run publish`. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 2bcdd77dc36..374dbf17958 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,5 +1,25 @@ If you are reporting a bug, please fill in below. Otherwise feel free to remove this template entirely. +### Can you reproduce the problem with latest npm? + +Many errors, especially related to "missing modules", are due to npm bugs. + +If you're using Windows, [follow these instructions to update npm](https://github.com/npm/npm/wiki/Troubleshooting#upgrading-on-windows). + +If you're using OS X or Linux, run this to update npm: + +``` +npm install -g npm@latest + +cd your_project_directory +rm -rf node_modules +npm install +``` + +Then try to reproduce the issue again. + +Can you still reproduce it? + ### Description What are you reporting? diff --git a/README.md b/README.md index 334dd477e85..9ad9303c5bc 100644 --- a/README.md +++ b/README.md @@ -102,23 +102,29 @@ 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) +- [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) +- [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 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) - [Adding Bootstrap](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-bootstrap) - [Adding Flow](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-flow) - [Adding Custom Environment Variables](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables) - [Can I Use Decorators?](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#can-i-use-decorators) -- [Integrating with a Node Backend](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#integrating-with-a-node-backend) +- [Integrating with an API Backend](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md##integrating-with-an-api-backend) - [Proxying API Requests in Development](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development) - [Using HTTPS in Development](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-https-in-development) - [Generating Dynamic `<meta>` Tags on the Server](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#generating-dynamic-meta-tags-on-the-server) - [Running Tests](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) +- [Developing Components in Isolation](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#developing-components-in-isolation) +- [Making a Progressive Web App](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) - [Deployment](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment) +- [Troubleshooting](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting) A copy of the user guide will be created as `README.md` in your project folder. @@ -130,7 +136,7 @@ Please refer to the [User Guide](https://github.com/facebookincubator/create-rea * **One Dependency:** There is just one build dependency. It uses Webpack, Babel, ESLint, and other amazing projects, but provides a cohesive curated experience on top of them. -* **Zero Configuration:** There are no configuration files or command line options. Configuring both development and production builds is handled for you so you can focus on writing code. +* **Convention over Configuration:** You don't need to configure anything by default. Reasonably good configuration of both development and production builds is handled for you so you can focus on writing code. * **No Lock-In:** You can “eject” to a custom setup at any time. Run a single command, and all the configuration and build dependencies will be moved directly into your project, so you can pick up right where you left off. @@ -138,7 +144,7 @@ Please refer to the [User Guide](https://github.com/facebookincubator/create-rea **If you’re getting started** with React, use `create-react-app` to automate the build of your app. There is no configuration file, and `react-scripts` is the only extra build dependency in your `package.json`. Your environment will have everything you need to build a modern React app: -* React, JSX, and ES6 support. +* React, JSX, ES6, and Flow syntax support. * Language extras beyond ES6 like the object spread operator. * A dev server that lints for common errors. * Import CSS and image files directly from JavaScript. @@ -205,6 +211,8 @@ Some of the more popular and actively maintained ones are: * [insin/nwb](https://github.com/insin/nwb) * [mozilla/neo](https://github.com/mozilla/neo) * [NYTimes/kyt](https://github.com/NYTimes/kyt) +* [zeit/next.js](https://github.com/zeit/next.js) +* [gatsbyjs/gatsby](https://github.com/gatsbyjs/gatsby) Notable alternatives also include: diff --git a/lerna.json b/lerna.json index efe8073fd0c..00b2a8875a0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,4 +1,16 @@ { - "lerna": "2.0.0-beta.29", - "version": "independent" + "lerna": "2.0.0-beta.30", + "version": "independent", + "changelog": { + "repo": "facebookincubator/create-react-app", + "labels": { + "tag: new feature": ":rocket: New Feature", + "tag: breaking change": ":boom: Breaking Change", + "tag: bug fix": ":bug: Bug Fix", + "tag: enhancement": ":nail_care: Enhancement", + "tag: documentation": ":memo: Documentation", + "tag: internal": ":house: Internal" + }, + "cacheDir": ".changelog" + } } diff --git a/package.json b/package.json index 1029bc47758..6645ea27971 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "private": true, "scripts": { "build": "node packages/react-scripts/scripts/build.js", + "changelog": "lerna-changelog", "create-react-app": "tasks/cra.sh", "e2e": "tasks/e2e.sh", "postinstall": "lerna bootstrap", @@ -12,11 +13,12 @@ "devDependencies": { "babel-eslint": "6.1.2", "eslint": "3.5.0", - "eslint-config-react-app": "file:packages/eslint-config-react-app", + "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.29" + "lerna": "2.0.0-beta.30", + "lerna-changelog": "^0.2.3" } } diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md index c7548b84531..5221c41af59 100644 --- a/packages/babel-preset-react-app/README.md +++ b/packages/babel-preset-react-app/README.md @@ -23,3 +23,5 @@ Then create a file named `.babelrc` with following contents in the root folder o "presets": ["react-app"] } ``` + +This preset uses the `useBuiltIns` option with [transform-object-rest-spread](http://babeljs.io/docs/plugins/transform-object-rest-spread/) and [transform-react-jsx](http://babeljs.io/docs/plugins/transform-react-jsx/), which assumes that `Object.assign` is available or polyfilled. diff --git a/packages/babel-preset-react-app/index.js b/packages/babel-preset-react-app/index.js index d3063aee304..a028babc06c 100644 --- a/packages/babel-preset-react-app/index.js +++ b/packages/babel-preset-react-app/index.js @@ -11,24 +11,27 @@ var path = require('path'); const plugins = [ - // class { handleClick = () => { } } - require.resolve('babel-plugin-transform-class-properties'), - // { ...todo, completed: true } - require.resolve('babel-plugin-transform-object-rest-spread'), - // function* () { yield 42; yield 43; } - [require.resolve('babel-plugin-transform-regenerator'), { - // Async functions are converted to generators by babel-preset-latest - async: false - }], - // Polyfills the runtime needed for async/await and generators - [require.resolve('babel-plugin-transform-runtime'), { - helpers: false, - polyfill: false, - regenerator: true, - // Resolve the Babel runtime relative to the config. - moduleName: path.dirname(require.resolve('babel-runtime/package')) - }] - ]; + // class { handleClick = () => { } } + require.resolve('babel-plugin-transform-class-properties'), + // The following two plugins use Object.assign directly, instead of Babel's + // extends helper. Note that this assumes `Object.assign` is available. + // { ...todo, completed: true } + [require.resolve('babel-plugin-transform-object-rest-spread'), { + useBuiltIns: true + }], + // Transforms JSX + [require.resolve('babel-plugin-transform-react-jsx'), { + useBuiltIns: true + }], + // Polyfills the runtime needed for async/await and generators + [require.resolve('babel-plugin-transform-runtime'), { + helpers: false, + polyfill: false, + regenerator: true, + // Resolve the Babel runtime relative to the config. + moduleName: path.dirname(require.resolve('babel-runtime/package')) + }] +]; // This is similar to how `env` works in Babel: // https://babeljs.io/docs/usage/babelrc/#env-option @@ -46,6 +49,12 @@ if (env !== 'development' && env !== 'test' && env !== 'production') { } if (env === 'development' || env === 'test') { + // The following two plugins are currently necessary to make React warnings + // include more valuable information. They are included here because they are + // currently not enabled in babel-preset-react. See the below threads for more info: + // https://github.com/babel/babel/issues/4702 + // https://github.com/babel/babel/pull/3540#issuecomment-228673661 + // https://github.com/facebookincubator/create-react-app/issues/989 plugins.push.apply(plugins, [ // Adds component stack to warning messages require.resolve('babel-plugin-transform-react-jsx-source'), @@ -55,12 +64,19 @@ 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 [require('babel-preset-env').default, { targets: { - node: parseFloat(process.versions.node), + node: 'current', }, }], // JSX, Flow @@ -76,7 +92,13 @@ if (env === 'test') { // JSX, Flow require.resolve('babel-preset-react') ], - plugins: plugins + plugins: plugins.concat([ + // function* () { yield 42; yield 43; } + [require.resolve('babel-plugin-transform-regenerator'), { + // Async functions are converted to generators by babel-preset-latest + async: false + }], + ]) }; if (env === 'production') { @@ -91,4 +113,3 @@ if (env === 'test') { // ]); } } - diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index bd1db0f5722..c9513ae340a 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": "0.2.1", + "version": "2.0.1", "description": "Babel preset used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,16 +11,18 @@ "index.js" ], "dependencies": { - "babel-plugin-transform-class-properties": "6.11.5", - "babel-plugin-transform-object-rest-spread": "6.8.0", + "babel-plugin-transform-class-properties": "6.16.0", + "babel-plugin-transform-es2015-parameters": "6.18.0", + "babel-plugin-transform-object-rest-spread": "6.19.0", "babel-plugin-transform-react-constant-elements": "6.9.1", + "babel-plugin-transform-react-jsx": "6.8.0", "babel-plugin-transform-react-jsx-self": "6.11.0", "babel-plugin-transform-react-jsx-source": "6.9.0", "babel-plugin-transform-regenerator": "6.16.1", "babel-plugin-transform-runtime": "6.15.0", - "babel-preset-env": "0.0.4", - "babel-preset-latest": "6.14.0", - "babel-preset-react": "6.11.1", + "babel-preset-env": "0.0.8", + "babel-preset-latest": "6.16.0", + "babel-preset-react": "6.16.0", "babel-runtime": "6.11.6" } } diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js index d6478a13545..9ab11a5e938 100644 --- a/packages/create-react-app/index.js +++ b/packages/create-react-app/index.js @@ -38,53 +38,79 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var spawn = require('cross-spawn'); var chalk = require('chalk'); -var semver = require('semver'); -var argv = require('minimist')(process.argv.slice(2)); -var pathExists = require('path-exists'); -/** - * Arguments: - * --version - to print current version - * --verbose - to print logs while init - * --scripts-version <alternative package> - * Example of valid values: - * - a specific npm version: "0.22.0-rc1" - * - a .tgz archive from any npm repo: "https://registry.npmjs.org/react-scripts/-/react-scripts-0.20.0.tgz" - * - a package prepared with `tasks/clean_pack.sh`: "/Users/home/vjeux/create-react-app/react-scripts-0.22.0.tgz" - */ -var commands = argv._; -if (commands.length === 0) { - if (argv.version) { - console.log('create-react-app version: ' + require('./package.json').version); - process.exit(); - } +var currentNodeVersion = process.versions.node +if (currentNodeVersion.split('.')[0] < 4) { console.error( - 'Usage: create-react-app <project-directory> [--verbose]' + chalk.red( + 'You are running Node ' + currentNodeVersion + '.\n' + + 'Create React App requires Node 4 or higher. \n' + + 'Please update your version of Node.' + ) ); process.exit(1); } -createApp(commands[0], argv.verbose, argv['scripts-version']); +var fs = require('fs-extra'); +var path = require('path'); +var execSync = require('child_process').execSync; +var spawn = require('cross-spawn'); +var semver = require('semver'); + +var projectName; + +var program = require('commander') + .version(require('./package.json').version) + .arguments('<project-directory>') + .usage(chalk.green('<project-directory>') + ' [options]') + .action(function (name) { + projectName = name; + }) + .option('--verbose', 'print additional logs') + .option('--scripts-version <alternative-package>', 'use a non-standard version of react-scripts') + .on('--help', function () { + console.log(' Only ' + chalk.green('<project-directory>') + ' is required.'); + console.log(); + console.log(' A custom ' + chalk.cyan('--scripts-version') + ' can be one of:'); + console.log(' - a specific npm version: ' + chalk.green('0.8.2')); + console.log(' - a custom fork published on npm: ' + chalk.green('my-react-scripts')); + console.log(' - a .tgz archive: ' + chalk.green('https://mysite.com/my-react-scripts-0.8.2.tgz')); + console.log(' It is not needed unless you specifically want to use a fork.'); + console.log(); + console.log(' If you have any problems, do not hesitate to file an issue:'); + console.log(' ' + chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new')); + console.log(); + }) + .parse(process.argv) + +if (typeof projectName === 'undefined') { + console.error('Please specify the project directory:'); + console.log(' ' + chalk.cyan(program.name()) + chalk.green(' <project-directory>')); + console.log(); + console.log('For example:'); + console.log(' ' + chalk.cyan(program.name()) + chalk.green(' my-react-app')); + console.log(); + console.log('Run ' + chalk.cyan(program.name() + ' --help') + ' to see all options.'); + process.exit(1); +} + +createApp(projectName, program.verbose, program.scriptsVersion); function createApp(name, verbose, version) { var root = path.resolve(name); var appName = path.basename(root); checkAppName(appName); - - if (!pathExists.sync(name)) { - fs.mkdirSync(root); - } else if (!isSafeToCreateProjectIn(root)) { - console.log('The directory `' + name + '` contains file(s) that could conflict. Aborting.'); + fs.ensureDirSync(name); + if (!isSafeToCreateProjectIn(root)) { + console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.'); + console.log('Try using a new directory name.'); process.exit(1); } console.log( - 'Creating a new React app in ' + root + '.' + 'Creating a new React app in ' + chalk.green(root) + '.' ); console.log(); @@ -101,27 +127,50 @@ function createApp(name, verbose, version) { process.chdir(root); console.log('Installing packages. This might take a couple minutes.'); - console.log('Installing react-scripts from npm...'); + console.log('Installing ' + chalk.cyan('react-scripts') + '...'); console.log(); run(root, appName, version, verbose, originalDirectory); } +function shouldUseYarn() { + try { + execSync('yarnpkg --version', {stdio: 'ignore'}); + return true; + } catch (e) { + return false; + } +} + +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]; + } + + if (verbose) { + args.push('--verbose'); + } + + var child = spawn(command, args, {stdio: 'inherit'}); + child.on('close', function(code) { + callback(code, command, args); + }); +} + function run(root, appName, version, verbose, originalDirectory) { - var installPackage = getInstallPackage(version); - var packageName = getPackageName(installPackage); - var args = [ - 'install', - verbose && '--verbose', - '--save-dev', - '--save-exact', - installPackage, - ].filter(function(e) { return e; }); - var proc = spawn('npm', args, {stdio: 'inherit'}); - proc.on('close', function (code) { + var packageToInstall = getInstallPackage(version); + var packageName = getPackageName(packageToInstall); + + install(packageToInstall, verbose, function(code, command, args) { if (code !== 0) { - console.error('`npm ' + args.join(' ') + '` failed'); - return; + console.error(chalk.cyan(command + ' ' + args.join(' ')) + ' failed'); + process.exit(1); } checkNodeVersion(packageName); @@ -154,7 +203,7 @@ function getInstallPackage(version) { function getPackageName(installPackage) { if (installPackage.indexOf('.tgz') > -1) { // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz - // However, this function returns package name only wihout semver version. + // However, this function returns package name only without semver version. return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1]; } else if (installPackage.indexOf('@') > 0) { // Do not match @scope/ when stripping off @version or @tag @@ -178,8 +227,9 @@ function checkNodeVersion(packageName) { if (!semver.satisfies(process.version, packageJson.engines.node)) { console.error( chalk.red( - 'You are currently running Node %s but create-react-app requires %s.' + - ' Please use a supported version of Node.\n' + 'You are running Node %s.\n' + + 'Create React App requires Node %s or higher. \n' + + 'Please update your version of Node.' ), process.version, packageJson.engines.node @@ -197,7 +247,7 @@ function checkAppName(appName) { if (allDependencies.indexOf(appName) >= 0) { console.error( chalk.red( - 'We cannot create a project called `' + appName + '` because a dependency with the same name exists.\n' + + 'We cannot create a project called ' + chalk.green(appName) + ' because a dependency with the same name exists.\n' + 'Due to the way npm works, the following names are not allowed:\n\n' ) + chalk.cyan( diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json index 5094b9c78dd..0ccb85e786b 100644 --- a/packages/create-react-app/package.json +++ b/packages/create-react-app/package.json @@ -1,6 +1,6 @@ { "name": "create-react-app", - "version": "0.5.0", + "version": "1.0.3", "keywords": [ "react" ], @@ -21,9 +21,9 @@ }, "dependencies": { "chalk": "^1.1.1", + "commander": "^2.9.0", "cross-spawn": "^4.0.0", - "minimist": "^1.2.0", - "path-exists": "^2.1.0", + "fs-extra": "^1.0.0", "semver": "^5.0.3" } } diff --git a/packages/eslint-config-react-app/README.md b/packages/eslint-config-react-app/README.md index e4fac03e54d..5c20f50ca2e 100644 --- a/packages/eslint-config-react-app/README.md +++ b/packages/eslint-config-react-app/README.md @@ -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@6.1.2 eslint@3.5.0 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 + 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 ``` 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 7689a83544b..d93478bc8c9 100644 --- a/packages/eslint-config-react-app/index.js +++ b/packages/eslint-config-react-app/index.js @@ -21,8 +21,7 @@ module.exports = { parser: 'babel-eslint', - // import plugin is temporarily disabled, scroll below to see why - plugins: [/*'import', */'flowtype', 'jsx-a11y', 'react'], + plugins: ['import', 'flowtype', 'jsx-a11y', 'react'], env: { browser: true, @@ -44,8 +43,7 @@ module.exports = { settings: { 'import/ignore': [ - 'node_modules', - '\\.(json|css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$', + 'node_modules' ], 'import/extensions': ['.js'], 'import/resolver': { @@ -170,6 +168,9 @@ module.exports = { // This is probably fixable with a patch to eslint-loader. // When file A is saved, we want to invalidate all files that import it // *and* that currently have lint errors. This should fix the problem. + // (As an exception, import/no-webpack-loader-syntax can be enabled already + // because it doesn't depend on whether the file exists, so this issue + // doesn't apply to it.) // 'import/default': 'warn', // 'import/export': 'warn', @@ -181,11 +182,14 @@ module.exports = { // 'import/no-named-as-default': 'warn', // 'import/no-named-as-default-member': 'warn', // 'import/no-unresolved': ['warn', { commonjs: true }], + // We don't support configuring Webpack using import source strings, so this + // is always an error. + 'import/no-webpack-loader-syntax': 'error', // https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules 'react/jsx-equals-spacing': ['warn', 'never'], 'react/jsx-no-duplicate-props': ['warn', { ignoreCase: true }], - 'react/jsx-no-undef': 'warn', + 'react/jsx-no-undef': 'error', 'react/jsx-pascal-case': ['warn', { allowAllCaps: true, ignore: [], diff --git a/packages/eslint-config-react-app/package.json b/packages/eslint-config-react-app/package.json index 90c6a21f269..66e690e90f2 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.2.1", + "version": "0.5.0", "description": "ESLint configuration used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,11 +11,11 @@ "index.js" ], "peerDependencies": { - "babel-eslint": "6.1.2", - "eslint": "3.5.0", - "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" + "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" } } diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md index 14c69493e5a..455996fcc59 100644 --- a/packages/react-dev-utils/README.md +++ b/packages/react-dev-utils/README.md @@ -21,7 +21,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`. -It works in tandem with [HtmlWebpackPlugin](https://github.com/ampedandwired/html-dev-plugin) 2.x via its [events](https://github.com/ampedandwired/html-dev-plugin#events). +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 var path = require('path'); @@ -117,6 +117,7 @@ Extracts and prettifies warning and error messages from webpack [stats](https:// ```js var webpack = require('webpack'); var config = require('../config/webpack.config.dev'); +var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); var compiler = webpack(config); @@ -132,16 +133,32 @@ compiler.plugin('done', function(stats) { } if (messages.errors.length) { console.log('Failed to compile.'); - messages.errors.forEach(console.log); + messages.errors.forEach(e => console.log(e)); return; } if (messages.warnings.length) { console.log('Compiled with warnings.'); - messages.warnings.forEach(console.log); + messages.warnings.forEach(w => console.log(w)); } }); ``` +#### `getProcessForPort(port: number): string` + +Finds the currently running process on `port`. +Returns a string containing the name and directory, e.g., + +``` +create-react-app +in /Users/developer/create-react-app +``` + +```js +var getProcessForPort = require('react-dev-utils/getProcessForPort'); + +getProcessForPort(3000); +``` + #### `openBrowser(url: string): boolean` Attempts to open the browser with a given URL. @@ -168,6 +185,7 @@ You can control the behavior on `<Enter>` with `isYesDefault`. ```js var prompt = require('react-dev-utils/prompt'); + prompt( 'Are you sure you want to eat all the candy?', /* isYesDefault */ false diff --git a/packages/react-dev-utils/clearConsole.js b/packages/react-dev-utils/clearConsole.js index e37af65543a..cfd10155167 100644 --- a/packages/react-dev-utils/clearConsole.js +++ b/packages/react-dev-utils/clearConsole.js @@ -7,12 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -var isFirstClear = true; function clearConsole() { - // On first run, clear completely so it doesn't show half screen on Windows. - // On next runs, use a different sequence that properly scrolls back. - process.stdout.write(isFirstClear ? '\x1bc' : '\x1b[2J\x1b[0f'); - isFirstClear = false; + process.stdout.write(process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'); } module.exports = clearConsole; diff --git a/packages/react-dev-utils/formatWebpackMessages.js b/packages/react-dev-utils/formatWebpackMessages.js index d72d5f734ca..88834a9fc62 100644 --- a/packages/react-dev-utils/formatWebpackMessages.js +++ b/packages/react-dev-utils/formatWebpackMessages.js @@ -24,12 +24,6 @@ function isLikelyASyntaxError(message) { function formatMessage(message) { var lines = message.split('\n'); - // line #0 is filename - // line #1 is the main error message - if (!lines[0] || !lines[1]) { - return message; - } - // Remove webpack-specific loader notation from filename. // Before: // ./~/css-loader!./~/postcss-loader!./src/App.css @@ -39,6 +33,12 @@ function formatMessage(message) { lines[0] = lines[0].substr(lines[0].lastIndexOf('!') + 1); } + // line #0 is filename + // line #1 is the main error message + if (!lines[0] || !lines[1]) { + return lines.join('\n'); + } + // Cleans up verbose "module not found" messages for files and packages. if (lines[1].indexOf('Module not found: ') === 0) { lines = [ @@ -101,9 +101,12 @@ function formatMessage(message) { // Reassemble the message. message = lines.join('\n'); - // Internal stacks are generally useless so we strip them + // Internal stacks are generally useless so we strip them... with the + // exception of stacks containing `webpack:` because they're normally + // from user code generated by WebPack. For more information see + // https://github.com/facebookincubator/create-react-app/pull/1050 message = message.replace( - /^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '' + /^\s*at\s((?!webpack:).)*:\d+:\d+[\s\)]*(\n|$)/gm, '' ); // at ... ...:x:y return message; diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js new file mode 100644 index 00000000000..5540fbad47a --- /dev/null +++ b/packages/react-dev-utils/getProcessForPort.js @@ -0,0 +1,61 @@ +var chalk = require('chalk'); +var execSync = require('child_process').execSync; +var path = require('path'); + +var execOptions = { + encoding: 'utf8', + stdio: [ + 'pipe', // stdin (default) + 'pipe', // stdout (default) + 'ignore' //stderr + ] +}; + +function isProcessAReactApp(processCommand) { + return /^node .*react-scripts\/scripts\/start\.js\s?$/.test(processCommand); +} + +function getProcessIdOnPort(port) { + return execSync('lsof -i:' + port + ' -P -t -sTCP:LISTEN', execOptions).trim(); +} + +function getPackageNameInDirectory(directory) { + var packagePath = path.join(directory.trim(), 'package.json'); + + try { + return require(packagePath).name; + } catch(e) { + return null; + } + +} + +function getProcessCommand(processId, processDirectory) { + var command = execSync('ps -o command -p ' + processId + ' | sed -n 2p', execOptions); + + if (isProcessAReactApp(command)) { + const packageName = getPackageNameInDirectory(processDirectory); + return (packageName) ? packageName + '\n' : command; + } else { + return command; + } + +} + +function getDirectoryOfProcessById(processId) { + return execSync('lsof -p '+ processId + ' | grep cwd | awk \'{print $9}\'', execOptions).trim(); +} + +function getProcessForPort(port) { + try { + var processId = getProcessIdOnPort(port); + var directory = getDirectoryOfProcessById(processId); + var command = getProcessCommand(processId, directory); + return chalk.cyan(command) + chalk.blue(' in ') + chalk.cyan(directory); + } catch(e) { + return null; + } +} + +module.exports = getProcessForPort; + diff --git a/packages/react-dev-utils/openBrowser.js b/packages/react-dev-utils/openBrowser.js index 76b33a5924a..a3623515e0a 100644 --- a/packages/react-dev-utils/openBrowser.js +++ b/packages/react-dev-utils/openBrowser.js @@ -10,8 +10,32 @@ var execSync = require('child_process').execSync; var opn = require('opn'); +// https://github.com/sindresorhus/opn#app +var OSX_CHROME = 'google chrome'; + function openBrowser(url) { - if (process.platform === 'darwin') { + // 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; + + // Special case: BROWSER="none" will prevent opening completely. + if (browser === 'none') { + return false; + } + + // If we're on OS X, the user hasn't specifically + // requested a different browser, we can try opening + // Chrome with AppleScript. This lets us reuse an + // existing tab when possible instead of creating a new one. + const shouldTryOpenChromeWithAppleScript = ( + process.platform === 'darwin' && ( + typeof browser !== 'string' || + browser === OSX_CHROME + ) + ); + + if (shouldTryOpenChromeWithAppleScript) { try { // Try our best to reuse existing tab // on OS X Google Chrome with AppleScript @@ -25,10 +49,12 @@ function openBrowser(url) { // Ignore errors. } } + // Fallback to opn // (It will always open new tab) try { - opn(url); + var options = {app: browser}; + opn(url, options).catch(() => {}); // Prevent `unhandledRejection` error. return true; } catch (err) { return false; diff --git a/packages/react-dev-utils/openChrome.applescript b/packages/react-dev-utils/openChrome.applescript index 4dfec4a2657..0f56027213b 100644 --- a/packages/react-dev-utils/openChrome.applescript +++ b/packages/react-dev-utils/openChrome.applescript @@ -7,6 +7,10 @@ This source code is licensed under the BSD-style license found in the of patent rights can be found in the PATENTS file in the same directory. *) +property targetTab: null +property targetTabIndex: -1 +property targetWindow: null + on run argv set theURL to item 1 of argv @@ -16,14 +20,57 @@ on run argv make new window end if - -- Find a tab currently running the debugger + -- 1: Looking for tab running debugger + -- then, Reload debugging tab if found + -- then return + set found to my lookupTabWithUrl(theURL) + if found then + set targetWindow's active tab index to targetTabIndex + tell targetTab to reload + tell targetWindow to activate + set index of targetWindow to 1 + return + end if + + -- 2: Looking for Empty tab + -- In case debugging tab was not found + -- We try to find an empty tab instead + set found to my lookupTabWithUrl("chrome://newtab/") + if found then + set targetWindow's active tab index to targetTabIndex + set URL of targetTab to theURL + tell targetWindow to activate + return + end if + + -- 3: Create new tab + -- both debugging and empty tab were not found + -- make a new tab with url + tell window 1 + activate + make new tab with properties {URL:theURL} + end tell + end tell +end run + +-- Function: +-- Lookup tab with given url +-- if found, store tab, index, and window in properties +-- (properties were declared on top of file) +on lookupTabWithUrl(lookupUrl) + tell application "Chrome" + -- Find a tab with the given url set found to false set theTabIndex to -1 repeat with theWindow in every window set theTabIndex to 0 repeat with theTab in every tab of theWindow set theTabIndex to theTabIndex + 1 - if theTab's URL is theURL then + if (theTab's URL as string) contains lookupUrl then + -- assign tab, tab index, and window to properties + set targetTab to theTab + set targetTabIndex to theTabIndex + set targetWindow to theWindow set found to true exit repeat end if @@ -33,16 +80,6 @@ on run argv exit repeat end if end repeat - - if found then - tell theTab to reload - set index of theWindow to 1 - set theWindow's active tab index to theTabIndex - else - tell window 1 - activate - make new tab with properties {URL:theURL} - end tell - end if end tell -end run + return found +end lookupTabWithUrl diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 8de715d1916..f3062c56d19 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.2.1", + "version": "0.4.2", "description": "Webpack utilities used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -14,6 +14,7 @@ "clearConsole.js", "checkRequiredFiles.js", "formatWebpackMessages.js", + "getProcessForPort.js", "InterpolateHtmlPlugin.js", "openChrome.applescript", "openBrowser.js", @@ -27,10 +28,7 @@ "escape-string-regexp": "1.0.5", "html-entities": "1.2.0", "opn": "4.0.2", - "sockjs-client": "1.0.3", + "sockjs-client": "1.0.1", "strip-ansi": "3.0.1" - }, - "peerDependencies": { - "webpack": "^1.13.2" } } diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js index f15fd06f291..7b1768d8fa0 100644 --- a/packages/react-dev-utils/webpackHotDevClient.js +++ b/packages/react-dev-utils/webpackHotDevClient.js @@ -248,6 +248,7 @@ connection.onmessage = function(e) { case 'hash': handleAvailableHash(message.data); break; + case 'still-ok': case 'ok': handleSuccess(); break; diff --git a/packages/react-scripts/config/jest/transform.js b/packages/react-scripts/config/jest/babelTransform.js similarity index 84% rename from packages/react-scripts/config/jest/transform.js rename to packages/react-scripts/config/jest/babelTransform.js index 11a0149f97d..145bd86cc9a 100644 --- a/packages/react-scripts/config/jest/transform.js +++ b/packages/react-scripts/config/jest/babelTransform.js @@ -9,5 +9,6 @@ const babelJest = require('babel-jest'); module.exports = babelJest.createTransformer({ - presets: [require.resolve('babel-preset-react-app')] + presets: [require.resolve('babel-preset-react-app')], + babelrc: false }); diff --git a/packages/react-scripts/config/jest/FileStub.js b/packages/react-scripts/config/jest/cssTransform.js similarity index 52% rename from packages/react-scripts/config/jest/FileStub.js rename to packages/react-scripts/config/jest/cssTransform.js index c95c3e790a1..eead954406b 100644 --- a/packages/react-scripts/config/jest/FileStub.js +++ b/packages/react-scripts/config/jest/cssTransform.js @@ -5,9 +5,18 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow */ // @remove-on-eject-end -module.exports = "test-file-stub"; +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/tutorial-webpack.html + +module.exports = { + process() { + return 'module.exports = {};'; + }, + getCacheKey(fileData, filename) { + // The output is always the same. + return 'cssTransform'; + }, +}; diff --git a/packages/react-scripts/config/jest/CSSStub.js b/packages/react-scripts/config/jest/fileTransform.js similarity index 54% rename from packages/react-scripts/config/jest/CSSStub.js rename to packages/react-scripts/config/jest/fileTransform.js index 05810269ac6..82c672bebd3 100644 --- a/packages/react-scripts/config/jest/CSSStub.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -5,9 +5,16 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow */ // @remove-on-eject-end -module.exports = {}; +const path = require('path'); + +// This is a custom Jest transformer turning file imports into filenames. +// http://facebook.github.io/jest/docs/tutorial-webpack.html + +module.exports = { + process(src, filename) { + return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; + }, +}; diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 1c154c36164..2f10ea2fb8a 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -30,9 +30,14 @@ function resolveApp(relativePath) { // It will then be used by Webpack configs. // Jest doesn’t need this because it already handles `NODE_PATH` out of the box. +// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. +// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. +// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 + var nodePaths = (process.env.NODE_PATH || '') .split(process.platform === 'win32' ? ';' : ':') .filter(Boolean) + .filter(folder => !path.isAbsolute(folder)) .map(resolveApp); // config after eject: we're in ./config/ @@ -43,6 +48,7 @@ module.exports = { appIndexJs: resolveApp('src/index.js'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), + yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveApp('src/setupTests.js'), appNodeModules: resolveApp('node_modules'), ownNodeModules: resolveApp('node_modules'), @@ -62,13 +68,13 @@ module.exports = { appIndexJs: resolveApp('src/index.js'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), + yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveApp('src/setupTests.js'), appNodeModules: resolveApp('node_modules'), // this is empty with npm3 but node resolution searches higher anyway: ownNodeModules: resolveOwn('../node_modules'), nodePaths: nodePaths }; -// @remove-on-eject-end // config before publish: we're in ./packages/react-scripts/config/ if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) { @@ -79,9 +85,11 @@ if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) 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 }; } +// @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 8a8b59ed859..96fd632b795 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -9,10 +9,8 @@ */ // @remove-on-eject-end -var path = require('path'); var autoprefixer = require('autoprefixer'); var webpack = require('webpack'); -var findCacheDir = require('find-cache-dir'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); @@ -20,6 +18,11 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod var getClientEnvironment = require('./env'); var paths = require('./paths'); +// @remove-on-eject-begin +// `path` is not used after eject - see https://github.com/facebookincubator/create-react-app/issues/1174 +var path = require('path'); +// @remove-on-eject-end + // Webpack uses `publicPath` to determine where the app is being served from. // In development, we always serve from the root. This makes config easier. var publicPath = '/'; @@ -34,11 +37,9 @@ var env = getClientEnvironment(publicUrl); // It is focused on developer experience and fast rebuilds. // The production configuration is different and lives in a separate file. module.exports = { - // This makes the bundle appear split into separate modules in the devtools. - // We don't use source maps here because they can be confusing: - // https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875 - // You may want 'cheap-module-source-map' instead if you prefer source maps. - devtool: 'eval', + // You may want 'eval' instead if you prefer to see the compiled output in DevTools. + // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343. + devtool: 'cheap-module-source-map', // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. // The first two entry points enable "hot" CSS and auto-refreshes for JS. @@ -111,6 +112,34 @@ module.exports = { } ], loaders: [ + // Default loader: load all assets that are not handled + // by other loaders with the url loader. + // Note: This list needs to be updated with every change of extensions + // the other loaders match. + // E.g., when adding a loader for a new supported file extension, + // we need to add the supported extension to this loader too. + // Add one new line in `exclude` for each loader. + // + // "file" loader makes sure those assets get served by WebpackDevServer. + // When you `import` an asset, you get its (virtual) filename. + // In production, they would get copied to the `build` folder. + // "url" loader works like "file" loader except that it embeds assets + // smaller than specified limit in bytes as data URLs to avoid requests. + // A missing `test` is equivalent to a match. + { + exclude: [ + /\.html$/, + /\.(js|jsx)$/, + /\.css$/, + /\.json$/, + /\.svg$/ + ], + loader: 'url', + query: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]' + } + }, // Process JS with Babel. { test: /\.(js|jsx)$/, @@ -122,12 +151,9 @@ module.exports = { presets: [require.resolve('babel-preset-react-app')], // @remove-on-eject-end // This is a feature of `babel-loader` for webpack (not Babel itself). - // It enables caching results in ./node_modules/.cache/react-scripts/ - // directory for faster rebuilds. We use findCacheDir() because of: - // https://github.com/facebookincubator/create-react-app/issues/483 - cacheDirectory: findCacheDir({ - name: 'react-scripts' - }) + // It enables caching results in ./node_modules/.cache/babel-loader/ + // directory for faster rebuilds. + cacheDirectory: true } }, // "postcss" loader applies autoprefixer to our CSS. @@ -137,7 +163,7 @@ module.exports = { // in development "style" loader enables hot editing of CSS. { test: /\.css$/, - loader: 'style!css!postcss' + loader: 'style!css?importLoaders=1!postcss' }, // JSON is not enabled by default in Webpack but both Node and Browserify // allow it implicitly so we also enable it. @@ -145,25 +171,13 @@ module.exports = { test: /\.json$/, loader: 'json' }, - // "file" loader makes sure those assets get served by WebpackDevServer. - // When you `import` an asset, you get its (virtual) filename. - // In production, they would get copied to the `build` folder. + // "file" loader for svg { - test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, + test: /\.svg$/, loader: 'file', query: { name: 'static/media/[name].[hash:8].[ext]' } - }, - // "url" loader works just like "file" loader but it also embeds - // assets smaller than specified size as data URLs to avoid requests. - { - test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, - loader: 'url', - query: { - limit: 10000, - name: 'static/media/[name].[hash:8].[ext]' - } } ] }, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index fa787d43ea7..058db0d7921 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -9,7 +9,6 @@ */ // @remove-on-eject-end -var path = require('path'); var autoprefixer = require('autoprefixer'); var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -20,6 +19,11 @@ var url = require('url'); var paths = require('./paths'); var getClientEnvironment = require('./env'); +// @remove-on-eject-begin +// `path` is not used after eject - see https://github.com/facebookincubator/create-react-app/issues/1174 +var path = require('path'); +// @remove-on-eject-end + function ensureSlash(path, needsSlash) { var hasSlash = path.endsWith('/'); if (hasSlash && !needsSlash) { @@ -116,6 +120,32 @@ module.exports = { } ], loaders: [ + // Default loader: load all assets that are not handled + // by other loaders with the url loader. + // Note: This list needs to be updated with every change of extensions + // the other loaders match. + // E.g., when adding a loader for a new supported file extension, + // we need to add the supported extension to this loader too. + // Add one new line in `exclude` for each loader. + // + // "file" loader makes sure those assets end up in the `build` folder. + // When you `import` an asset, you get its filename. + // "url" loader works just like "file" loader but it also embeds + // assets smaller than specified size as data URLs to avoid requests. + { + exclude: [ + /\.html$/, + /\.(js|jsx)$/, + /\.css$/, + /\.json$/, + /\.svg$/ + ], + loader: 'url', + query: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]' + } + }, // Process JS with Babel. { test: /\.(js|jsx)$/, @@ -142,15 +172,7 @@ module.exports = { // in the main CSS file. { test: /\.css$/, - // "?-autoprefixer" disables autoprefixer in css-loader itself: - // https://github.com/webpack/css-loader/issues/281 - // We already have it thanks to postcss. We only pass this flag in - // production because "css" loader only enables autoprefixer-powered - // removal of unnecessary prefixes when Uglify plugin is enabled. - // Webpack 1.x uses Uglify plugin as a signal to minify *all* the assets - // including CSS. This is confusing and will be removed in Webpack 2: - // https://github.com/webpack/webpack/issues/283 - loader: ExtractTextPlugin.extract('style', 'css?-autoprefixer!postcss') + loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1!postcss') // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. }, // JSON is not enabled by default in Webpack but both Node and Browserify @@ -159,24 +181,13 @@ module.exports = { test: /\.json$/, loader: 'json' }, - // "file" loader makes sure those assets end up in the `build` folder. - // When you `import` an asset, you get its filename. + // "file" loader for svg { - test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, + test: /\.svg$/, loader: 'file', query: { name: 'static/media/[name].[hash:8].[ext]' } - }, - // "url" loader works just like "file" loader but it also embeds - // assets smaller than specified size as data URLs to avoid requests. - { - test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, - loader: 'url', - query: { - limit: 10000, - name: 'static/media/[name].[hash:8].[ext]' - } } ] }, diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 004ba9ee088..38a6f86dbd6 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -1,6 +1,6 @@ { "name": "react-scripts", - "version": "0.6.1", + "version": "0.8.5", "description": "Configuration and scripts for Create React App.", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -23,102 +23,53 @@ "react-scripts": "./bin/react-scripts.js" }, "dependencies": { - "autoprefixer": "6.4.1", - "babel-core": "6.14.0", - "babel-eslint": "6.1.2", - "babel-jest": "16.0.0", - "babel-loader": "6.2.5", - "babel-preset-react-app": "^0.2.1", + "autoprefixer": "6.5.1", + "babel-core": "6.17.0", + "babel-eslint": "7.1.1", + "babel-jest": "18.0.0", + "babel-loader": "6.2.10", + "babel-preset-react-app": "^2.0.1", "case-sensitive-paths-webpack-plugin": "1.1.4", "chalk": "1.1.3", "connect-history-api-fallback": "1.3.0", - "cross-spawn": "4.0.0", - "css-loader": "0.24.0", - "detect-port": "1.0.0", + "cross-spawn": "4.0.2", + "css-loader": "0.26.0", + "detect-port": "1.0.1", "dotenv": "2.0.0", - "eslint": "3.5.0", - "eslint-config-react-app": "^0.2.1", - "eslint-loader": "1.5.0", - "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", + "eslint": "3.8.1", + "eslint-config-react-app": "^0.5.0", + "eslint-loader": "1.6.0", + "eslint-plugin-flowtype": "2.21.0", + "eslint-plugin-import": "2.0.1", + "eslint-plugin-jsx-a11y": "2.2.3", + "eslint-plugin-react": "6.4.1", "extract-text-webpack-plugin": "1.0.1", "file-loader": "0.9.0", "filesize": "3.3.0", - "find-cache-dir": "0.1.1", "fs-extra": "0.30.0", "gzip-size": "3.0.0", - "html-webpack-plugin": "2.22.0", - "http-proxy-middleware": "0.17.1", - "jest": "16.0.1", + "html-webpack-plugin": "2.24.0", + "http-proxy-middleware": "0.17.2", + "jest": "18.0.0", "json-loader": "0.5.4", "object-assign": "4.1.0", - "path-exists": "2.1.0", - "postcss-loader": "0.13.0", + "postcss-loader": "1.0.0", "promise": "7.1.1", - "react-dev-utils": "^0.2.1", + "react-dev-utils": "^0.4.2", "recursive-readdir": "2.1.0", - "rimraf": "2.5.4", "strip-ansi": "3.0.1", "style-loader": "0.13.1", "url-loader": "0.5.7", - "webpack": "1.13.2", - "webpack-dev-server": "1.16.1", - "webpack-manifest-plugin": "1.0.1", + "webpack": "1.14.0", + "webpack-dev-server": "1.16.2", + "webpack-manifest-plugin": "1.1.0", "whatwg-fetch": "1.0.0" }, "devDependencies": { - "bundle-deps": "1.0.0", "react": "^15.3.0", "react-dom": "^15.3.0" }, "optionalDependencies": { "fsevents": "1.0.14" - }, - "bundledDependencies": [ - "autoprefixer", - "babel-core", - "babel-eslint", - "babel-jest", - "babel-loader", - "babel-preset-react-app", - "case-sensitive-paths-webpack-plugin", - "chalk", - "connect-history-api-fallback", - "cross-spawn", - "css-loader", - "detect-port", - "dotenv", - "eslint", - "eslint-config-react-app", - "eslint-loader", - "eslint-plugin-flowtype", - "eslint-plugin-import", - "eslint-plugin-jsx-a11y", - "eslint-plugin-react", - "extract-text-webpack-plugin", - "file-loader", - "filesize", - "find-cache-dir", - "fs-extra", - "gzip-size", - "html-webpack-plugin", - "http-proxy-middleware", - "jest", - "json-loader", - "object-assign", - "path-exists", - "postcss-loader", - "promise", - "react-dev-utils", - "recursive-readdir", - "rimraf", - "strip-ansi", - "style-loader", - "url-loader", - "webpack", - "webpack-dev-server", - "whatwg-fetch" - ] + } } diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index d0b92f6a73b..42be50d43a8 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -23,7 +23,6 @@ var fs = require('fs-extra'); var path = require('path'); var filesize = require('filesize'); var gzipSize = require('gzip-size').sync; -var rimrafSync = require('rimraf').sync; var webpack = require('webpack'); var config = require('../config/webpack.config.prod'); var paths = require('../config/paths'); @@ -31,6 +30,8 @@ var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); var recursive = require('recursive-readdir'); var stripAnsi = require('strip-ansi'); +var useYarn = fs.existsSync(paths.yarnLockFile); + // Warn and crash if required files are missing if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); @@ -75,7 +76,7 @@ recursive(paths.appBuild, (err, fileNames) => { // Remove all content but keep the directory so that // if you're in it, you don't end up in Trash - rimrafSync(paths.appBuild + '/*'); + fs.emptyDirSync(paths.appBuild); // Start the webpack build build(previousSizeMap); @@ -142,6 +143,11 @@ function build(previousSizeMap) { process.exit(1); } + if (process.env.CI && stats.compilation.warnings.length) { + printErrors('Failed to compile.', stats.compilation.warnings); + process.exit(1); + } + console.log(chalk.green('Compiled successfully.')); console.log(); @@ -151,7 +157,8 @@ function build(previousSizeMap) { console.log(); var openCommand = process.platform === 'win32' ? 'start' : 'open'; - var homepagePath = require(paths.appPackageJson).homepage; + var appPackage = require(paths.appPackageJson); + var homepagePath = appPackage.homepage; var publicPath = config.output.publicPath; if (homepagePath && homepagePath.indexOf('.github.io/') !== -1) { // "homepage": "http://user.github.io/project" @@ -160,20 +167,28 @@ function build(previousSizeMap) { console.log(); console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); console.log('To publish it at ' + chalk.green(homepagePath) + ', run:'); + // If script deploy has been added to package.json, skip the instructions + if (typeof appPackage.scripts.deploy === 'undefined') { + console.log(); + if (useYarn) { + console.log(' ' + chalk.cyan('yarn') + ' add --dev gh-pages'); + } else { + console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages'); + } + console.log(); + console.log('Add the following script in your ' + chalk.cyan('package.json') + '.'); + console.log(); + console.log(' ' + chalk.dim('// ...')); + console.log(' ' + chalk.yellow('"scripts"') + ': {'); + console.log(' ' + chalk.dim('// ...')); + console.log(' ' + chalk.yellow('"predeploy"') + ': ' + chalk.yellow('"npm run build",')); + console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"gh-pages -d build"')); + console.log(' }'); + console.log(); + console.log('Then run:'); + } console.log(); - console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages'); - console.log(); - console.log('Add the following script in your ' + chalk.cyan('package.json') + '.'); - console.log(); - console.log(' ' + chalk.dim('// ...')); - console.log(' ' + chalk.yellow('"scripts"') + ': {'); - console.log(' ' + chalk.dim('// ...')); - console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"gh-pages -d build"')); - console.log(' }'); - console.log(); - console.log('Then run:'); - console.log(); - console.log(' ' + chalk.cyan('npm') + ' run deploy'); + console.log(' ' + chalk.cyan(useYarn ? 'yarn' : 'npm') + ' run deploy'); console.log(); } else if (publicPath !== '/') { // "homepage": "http://mywebsite.com/project" @@ -200,7 +215,11 @@ function build(previousSizeMap) { 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(); - console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server'); + if (useYarn) { + console.log(' ' + chalk.cyan('yarn') + ' global add pushstate-server'); + } 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(); diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index d14aec6abef..86dbc15c3ae 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -8,10 +8,10 @@ */ var createJestConfig = require('../utils/createJestConfig'); -var fs = require('fs'); +var fs = require('fs-extra'); var path = require('path'); +var paths = require('../config/paths'); var prompt = require('react-dev-utils/prompt'); -var rimrafSync = require('rimraf').sync; var spawnSync = require('cross-spawn').sync; var chalk = require('chalk'); var green = chalk.green; @@ -30,36 +30,46 @@ prompt( var ownPath = path.join(__dirname, '..'); var appPath = path.join(ownPath, '..', '..'); + + function verifyAbsent(file) { + if (fs.existsSync(path.join(appPath, file))) { + console.error( + '`' + file + '` already exists in your app folder. We cannot ' + + 'continue as you would lose all the changes in that file or directory. ' + + 'Please move or delete it (maybe make a copy for backup) and run this ' + + 'command again.' + ); + process.exit(1); + } + } + + var folders = [ + 'config', + path.join('config', 'jest'), + 'scripts' + ]; + 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', 'CSSStub.js'), - path.join('config', 'jest', 'FileStub.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') ]; // Ensure that the app folder is clean and we won't override any files - files.forEach(function(file) { - if (fs.existsSync(path.join(appPath, file))) { - console.error( - '`' + file + '` already exists in your app folder. We cannot ' + - 'continue as you would lose all the changes in that file or directory. ' + - 'Please delete it (maybe make a copy for backup) and run this ' + - 'command again.' - ); - process.exit(1); - } - }); + folders.forEach(verifyAbsent); + files.forEach(verifyAbsent); // Copy the files over - fs.mkdirSync(path.join(appPath, 'config')); - fs.mkdirSync(path.join(appPath, 'config', 'jest')); - fs.mkdirSync(path.join(appPath, 'scripts')); + folders.forEach(function(folder) { + fs.mkdirSync(path.join(appPath, folder)) + }); console.log(); console.log(cyan('Copying files into ' + appPath)); @@ -133,9 +143,15 @@ prompt( ); console.log(); - console.log(cyan('Running npm install...')); - rimrafSync(ownPath); - spawnSync('npm', ['install'], {stdio: 'inherit'}); + 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!')); console.log(); diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index fa42f6dcee6..e438e5a2ee2 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -10,13 +10,13 @@ var fs = require('fs-extra'); var path = require('path'); var spawn = require('cross-spawn'); -var pathExists = require('path-exists'); var chalk = require('chalk'); module.exports = function(appPath, appName, verbose, originalDirectory) { var ownPackageName = require(path.join(__dirname, '..', 'package.json')).name; var ownPath = path.join(appPath, 'node_modules', ownPackageName); var appPackage = require(path.join(appPath, 'package.json')); + var useYarn = fs.existsSync(path.join(appPath, 'yarn.lock')); // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {}; @@ -35,7 +35,7 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { JSON.stringify(appPackage, null, 2) ); - var readmeExists = pathExists.sync(path.join(appPath, 'README.md')); + var readmeExists = fs.existsSync(path.join(appPath, 'README.md')); if (readmeExists) { fs.renameSync(path.join(appPath, 'README.md'), path.join(appPath, 'README.old.md')); } @@ -58,21 +58,31 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { } }); - // Run another npm install for react and react-dom - console.log('Installing react and react-dom from npm...'); + // Run yarn or npm for react and react-dom + // TODO: having to do two npm/yarn installs is bad, can we avoid it? + var command; + var args; + + if (useYarn) { + command = 'yarnpkg'; + args = ['add']; + } else { + command = 'npm'; + args = [ + 'install', + '--save', + verbose && '--verbose' + ].filter(function(e) { return e; }); + } + args.push('react', 'react-dom'); + + console.log('Installing react and react-dom using ' + command + '...'); console.log(); - // TODO: having to do two npm installs is bad, can we avoid it? - var args = [ - 'install', - 'react', - 'react-dom', - '--save', - verbose && '--verbose' - ].filter(function(e) { return e; }); - var proc = spawn('npm', args, {stdio: 'inherit'}); + + var proc = spawn(command, args, {stdio: 'inherit'}); proc.on('close', function (code) { if (code !== 0) { - console.error('`npm ' + args.join(' ') + '` failed'); + console.error('`' + command + ' ' + args.join(' ') + '` failed'); return; } @@ -91,23 +101,23 @@ module.exports = function(appPath, appName, verbose, originalDirectory) { console.log('Success! Created ' + appName + ' at ' + appPath); console.log('Inside that directory, you can run several commands:'); console.log(); - console.log(chalk.cyan(' npm start')); + console.log(chalk.cyan(' ' + command + ' start')); console.log(' Starts the development server.'); console.log(); - console.log(chalk.cyan(' npm run build')); + console.log(chalk.cyan(' ' + command + ' run build')); console.log(' Bundles the app into static files for production.'); console.log(); - console.log(chalk.cyan(' npm test')); + console.log(chalk.cyan(' ' + command + ' test')); console.log(' Starts the test runner.'); console.log(); - console.log(chalk.cyan(' npm run eject')); + 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('npm start')); + 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`')); diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 8723c281637..8615fb074a9 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -26,11 +26,17 @@ var detect = require('detect-port'); 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 useYarn = fs.existsSync(paths.yarnLockFile); +var cli = useYarn ? 'yarn' : 'npm'; +var isInteractive = process.stdout.isTTY; + // Warn and crash if required files are missing if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); @@ -64,29 +70,42 @@ function setupCompiler(host, port, protocol) { // 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() { - clearConsole(); + 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) { - clearConsole(); + 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)); - if (!messages.errors.length && !messages.warnings.length) { + 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('npm run build') + '.'); + console.log('To create a production build, use ' + chalk.cyan(cli + ' run build') + '.'); console.log(); + isFirstCompile = false; } // If errors exist, only show errors. @@ -176,18 +195,33 @@ function addMiddleware(devServer) { // - /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\/)).*$/; - devServer.use(mayProxy, - // Pass the scope regex both to Express and to the middleware for proxying - // of both HTTP and WebSockets to work without false positives. - httpProxyMiddleware(pathname => mayProxy.test(pathname), { - target: proxy, - logLevel: 'silent', - onError: onProxyError(proxy), - secure: false, - changeOrigin: true - }) - ); + + // 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); @@ -195,6 +229,8 @@ function addMiddleware(devServer) { 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', @@ -244,9 +280,12 @@ function runDevServer(host, port, protocol) { return console.log(err); } - clearConsole(); + if (isInteractive) { + clearConsole(); + } console.log(chalk.cyan('Starting the development server...')); console.log(); + openBrowser(protocol + '://' + host + ':' + port + '/'); }); } @@ -266,14 +305,20 @@ detect(DEFAULT_PORT).then(port => { return; } - clearConsole(); - var question = - chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + - '\n\nWould you like to run the app on another port instead?'; + if (isInteractive) { + clearConsole(); + var existingProcess = getProcessForPort(DEFAULT_PORT); + var question = + chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.' + + ((existingProcess) ? ' Probably:\n ' + existingProcess : '')) + + '\n\nWould you like to run the app on another port instead?'; - prompt(question, true).then(shouldChangePort => { - if (shouldChangePort) { - run(port); - } - }); + prompt(question, true).then(shouldChangePort => { + if (shouldChangePort) { + run(port); + } + }); + } else { + console.log(chalk.red('Something is already running on port ' + DEFAULT_PORT + '.')); + } }); diff --git a/packages/react-scripts/scripts/test.js b/packages/react-scripts/scripts/test.js index 90d1f835d54..9de5181d739 100644 --- a/packages/react-scripts/scripts/test.js +++ b/packages/react-scripts/scripts/test.js @@ -21,8 +21,8 @@ require('dotenv').config({silent: true}); const jest = require('jest'); const argv = process.argv.slice(2); -// Watch unless on CI -if (!process.env.CI) { +// Watch unless on CI or in coverage mode +if (!process.env.CI && argv.indexOf('--coverage') < 0) { argv.push('--watch'); } diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index 268265bdb6d..786937fb465 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -13,18 +13,26 @@ 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) +- [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) +- [Changing the Page `<title>`](#changing-the-page-title) - [Installing a Dependency](#installing-a-dependency) - [Importing a Component](#importing-a-component) - [Adding a Stylesheet](#adding-a-stylesheet) - [Post-Processing CSS](#post-processing-css) - [Adding Images and Fonts](#adding-images-and-fonts) - [Using the `public` Folder](#using-the-public-folder) + - [Changing the HTML](#changing-the-html) + - [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system) + - [When to Use the `public` Folder](#when-to-use-the-public-folder) +- [Using Global Variables](#using-global-variables) - [Adding Bootstrap](#adding-bootstrap) - [Adding Flow](#adding-flow) - [Adding Custom Environment Variables](#adding-custom-environment-variables) - [Can I Use Decorators?](#can-i-use-decorators) -- [Integrating with a Node Backend](#integrating-with-a-node-backend) +- [Integrating with an API Backend](#integrating-with-an-api-backend) + -[Node](#node) + -[Ruby on Rails](#ruby-on-rails) - [Proxying API Requests in Development](#proxying-api-requests-in-development) - [Using HTTPS in Development](#using-https-in-development) - [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) @@ -41,14 +49,24 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Continuous Integration](#continuous-integration) - [Disabling jsdom](#disabling-jsdom) - [Experimental Snapshot Testing](#experimental-snapshot-testing) + - [Editor Integration](#editor-integration) +- [Developing Components in Isolation](#developing-components-in-isolation) +- [Making a Progressive Web App](#making-a-progressive-web-app) - [Deployment](#deployment) + - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) - [Building for Relative Paths](#building-for-relative-paths) + - [Firebase](#firebase) - [GitHub Pages](#github-pages) - [Heroku](#heroku) - [Modulus](#modulus) - [Netlify](#netlify) - [Now](#now) + - [S3 and CloudFront](#s3-and-cloudfront) - [Surge](#surge) +- [Troubleshooting](#troubleshooting) + - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) + - [`npm run build` silently fails](#npm-run-build-silently-fails) + - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) - [Something Missing?](#something-missing) ## Updating to New Releases @@ -134,6 +152,8 @@ It correctly bundles React in production mode and optimizes the build for the be The build is minified and the filenames include the hashes.<br> Your app is ready to be deployed! +See the section about [deployment](#deployment) for more information. + ### `npm run eject` **Note: this is a one-way operation. Once you `eject`, you can’t go back!** @@ -144,6 +164,10 @@ 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. +## 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. + ## Displaying Lint Output in the Editor >Note: this feature is available with `react-scripts@0.2.0` and higher. @@ -174,11 +198,21 @@ 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.2.1 eslint@3.5.0 babel-eslint@6.1.2 eslint-plugin-react@6.3.0 eslint-plugin-import@1.12.0 eslint-plugin-jsx-a11y@2.2.2 eslint-plugin-flowtype@2.18.1 +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 ``` 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. +## Changing the Page `<title>` + +You can find the source HTML file in the `public` folder of the generated project. You may edit the `<title>` tag in it to change the title from “React App” to anything else. + +Note that normally you wouldn't edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML. + +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). + ## Installing a Dependency The generated project includes React and ReactDOM as dependencies. It also includes a set of scripts used by Create React App as a development dependency. You may install other dependencies (for example, React Router) with `npm`: @@ -320,7 +354,7 @@ function Header() { return <img src={logo} alt="Logo" />; } -export default function Header; +export default Header; ``` This ensures that when the project is built, Webpack will correctly move the images into the build folder, and provide us with correct paths. @@ -344,7 +378,18 @@ An alternative way of handling static assets is described in the next section. >Note: this feature is available with `react-scripts@0.5.0` and higher. -Normally we encourage you to `import` assets in JavaScript files as described above. This mechanism provides a number of benefits: +### Changing the HTML + +The `public` folder contains the HTML file so you can tweak it, for example, to [set the page title](#changing-the-page-title). +The `<script>` tag with the compiled code will be added to it automatically during the build process. + +### Adding Assets Outside of the Module System + +You can also add other assets to the `public` folder. + +Note that we normally encourage you to `import` assets in JavaScript files instead. +For example, see the sections on [adding a stylesheet](#adding-a-stylesheet) and [adding images and fonts](#adding-images-and-fonts). +This mechanism provides a number of benefits: * Scripts and stylesheets get minified and bundled together to avoid extra network requests. * Missing files cause compilation errors instead of 404 errors for your users. @@ -381,7 +426,31 @@ Keep in mind the downsides of this approach: * Missing files will not be called at compilation time, and will cause 404 errors for your users. * Result filenames won’t include content hashes so you’ll need to add query arguments or rename them every time they change. -However, it can be handy for referencing assets like [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest) from HTML, or including small scripts like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code. +### When to Use the `public` Folder + +Normally we recommend importing [stylesheets](#adding-a-stylesheet), [images, and fonts](#adding-images-and-fonts) from JavaScript. +The `public` folder is useful as a workaround for a number of less common cases: + +* You need a file with a specific name in the build output, such as [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest). +* You have thousands of images and need to dynamically reference their paths. +* You want to include a small script like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code. +* Some library may be incompatible with Webpack and you have no other option but to include it as a `<script>` tag. + +Note that if you add a `<script>` that declares global variables, you also need to read the next section on using them. + +## Using Global Variables + +When you include a script in the HTML file that defines global variables and try to use one of these variables in the code, the linter will complain because it cannot see the definition of the variable. + +You can avoid this by reading the global variable explicitly from the `window` object, for example: + +```js +const $ = window.$; +``` + +This makes it obvious you are using a global variable intentionally rather than because of a typo. + +Alternatively, you can force the linter to ignore any line by adding `// eslint-disable-line` after it. ## Adding Bootstrap @@ -411,28 +480,21 @@ Now you are ready to use the imported React Bootstrap components within your com ## Adding Flow -Flow typing is currently [not supported out of the box](https://github.com/facebookincubator/create-react-app/issues/72) with the default `.flowconfig` generated by Flow. If you run it, you might get errors like this: +Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. -```js -node_modules/fbjs/lib/Deferred.js.flow:60 - 60: Promise.prototype.done.apply(this._promise, arguments); - ^^^^ property `done`. Property not found in -495: declare class Promise<+R> { - ^ Promise. See lib: /private/tmp/flow/flowlib_34952d31/core.js:495 +Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. -node_modules/fbjs/lib/shallowEqual.js.flow:29 - 29: return x !== 0 || 1 / (x: $FlowIssue) === 1 / (y: $FlowIssue); - ^^^^^^^^^^ identifier `$FlowIssue`. Could not resolve name -``` +To add Flow to a Create React App project, follow these steps: -To fix this, change your `.flowconfig` to look like this: +1. Run `npm install --save-dev flow-bin`. +2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. +3. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). -```ini -[ignore] -<PROJECT_ROOT>/node_modules/fbjs/.* -``` +Now you can run `npm run flow` to check the files for type errors. +You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. +In the future we plan to integrate it into Create React App even more closely. -Re-run flow, and you shouldn’t get any extra issues. +To learn more about Flow, check out [its documentation](https://flowtype.org/). ## Adding Custom Environment Variables @@ -542,9 +604,19 @@ Please refer to these two threads for reference: Create React App will add decorator support when the specification advances to a stable stage. -## Integrating with a Node Backend +## Integrating with an API Backend + +These tutorials will help you to integrate your app with an API backend running on another port, +using `fetch()` to access it. -Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/) for instructions on integrating an app with a Node backend running on another port, and using `fetch()` to access it. You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). +### Node +Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). +You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). + +### Ruby on Rails + +Check out [this tutorial](https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/). +You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo-rails). ## Proxying API Requests in Development @@ -788,7 +860,11 @@ Note that tests run much slower with coverage so it is recommended to run it sep ### Continuous Integration -By default `npm test` runs the watcher with interactive CLI. However, you can force it to run tests once and finish the process by setting an environment variable called `CI`. Popular CI servers already set it by default but you can do this yourself too: +By default `npm test` runs the watcher with interactive CLI. However, you can force it to run tests once and finish the process by setting an environment variable called `CI`. + +When creating a build of your application with `npm run build` linter warnings are not checked by default. Like `npm test`, you can force the build to perform a linter warning check by setting the environment variable `CI`. If any warnings are encountered then the build fails. + +Popular CI servers already set the environment variable `CI` by default but you can do this yourself too: ### On CI servers #### Travis CI @@ -802,9 +878,10 @@ node_js: - 6 cache: directories: - - node_modules -script + - node_modules +script: - npm test + - npm run build ``` 1. Trigger your first build with a git push. 1. [Customize your Travis CI Build](https://docs.travis-ci.com/user/customizing-the-build/) if needed. @@ -816,6 +893,10 @@ script set CI=true&&npm test ``` +```cmd +set CI=true&&npm run build +``` + (Note: the lack of whitespace is intentional.) ##### Linux, OS X (Bash) @@ -824,9 +905,15 @@ set CI=true&&npm test CI=true npm test ``` -This way Jest will run tests once instead of launching the watcher. +```bash +CI=true npm run build +``` + +The test command will force Jest to run tests once instead of launching the watcher. + +> If you find yourself doing this often in development, please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new) to tell us about your use case because we want to make watcher the best experience and are open to changing how it works to accommodate more workflows. -If you find yourself doing this often in development, please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new) to tell us about your use case because we want to make watcher the best experience and are open to changing how it works to accommodate more workflows. +The build command will check for linter warnings and fail if any are found. ### Disabling jsdom @@ -861,9 +948,103 @@ Snapshot testing is a new feature of Jest that automatically generates text snap This feature is experimental and still [has major usage issues](https://github.com/facebookincubator/create-react-app/issues/372) so we only encourage you to use it if you like experimental technology. We intend to gradually improve it over time and eventually offer it as the default solution for testing React components, but this will take time. [Read more about snapshot testing.](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) +### Editor Integration + +If you use [Visual Studio Code](https://code.visualstudio.com), there is a [Jest extension](https://github.com/orta/vscode-jest) which works with Create React App out of the box. This provides a lot of IDE-like features while using a text editor: showing the status of a test run with potential fail messages inline, starting and stopping the watcher automatically, and offering one-click snapshot updates. + +![VS Code Jest Preview](https://cloud.githubusercontent.com/assets/49038/20795349/a032308a-b7c8-11e6-9b34-7eeac781003f.png) + +## Developing Components in Isolation + +Usually, in an app, you have a lot of UI components, and each of them has many different states. +For an example, a simple button component could have following states: + +* With a text label. +* With an emoji. +* In the disabled mode. + +Usually, it’s hard to see these states without running a sample app or some examples. + +Create React App doesn't include any tools for this by default, but you can easily add [React Storybook](https://github.com/kadirahq/react-storybook) to your project. **It is a third-party tool that lets you develop components and see all their states in isolation from your app**. + +![React Storybook Demo](http://i.imgur.com/7CIAWpB.gif) + +You can also deploy your Storybook as a static app. This way, everyone in your team can view and review different states of UI components without starting a backend server or creating an account in your app. + +**Here’s how to setup your app with Storybook:** + +First, install the following npm package globally: + +```sh +npm install -g getstorybook +``` + +Then, run the following command inside your app’s directory: + +```sh +getstorybook +``` + +After that, follow the instructions on the screen. + +Learn more about React Storybook: + +* Screencast: [Getting Started with React Storybook](https://egghead.io/lessons/react-getting-started-with-react-storybook) +* [GitHub Repo](https://github.com/kadirahq/react-storybook) +* [Documentation](https://getstorybook.io/docs) +* [Snapshot Testing](https://github.com/kadirahq/storyshots) with React Storybook + +## Making a Progressive Web App + +You can turn your React app into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) by following the steps in [this repository](https://github.com/jeffposnick/create-react-pwa). + ## Deployment -## Building for Relative Paths +`npm run build` creates a `build` directory with a production build of your app. Set up your favourite HTTP server so that a visitor to your site is served `index.html`, and requests to static paths like `/static/js/main.<hash>.js` are served with the contents of the `/static/js/main.<hash>.js` file. For example, Python contains a built-in HTTP server that can serve static files: + +```sh +cd build +python -m SimpleHTTPServer 9000 +``` + +If you're using [Node](https://nodejs.org/) and [Express](http://expressjs.com/) as a server, it might look like this: + +```javascript +const express = require('express'); +const path = require('path'); +const app = express(); + +app.use(express.static('./build')); + +app.get('/', function (req, res) { + res.sendFile(path.join(__dirname, './build', 'index.html')); +}); + +app.listen(9000); +``` + +Create React App is not opinionated about your choice of web server. Any static file server will do. The `build` folder with static assets is the only output produced by Create React App. + +However this is not quite enough if you use client-side routing. Read the next section if you want to support URLs like `/todos/42` in your single-page app. + +### Serving Apps with Client-Side Routing + +If you use routers that use the HTML5 [`pushState` history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries) under the hood (for example, [React Router](https://github.com/ReactTraining/react-router) with `browserHistory`), many static file servers will fail. For example, if you used React Router with a route for `/todos/42`, the development server will respond to `localhost:3000/todos/42` properly, but an Express serving a production build as above will not. + +This is because when there is a fresh page load for a `/todos/42`, the server looks for the file `build/todos/42` and does not find it. The server needs to be configured to respond to a request to `/todos/42` by serving `index.html`. For example, we can amend our Express example above to serve `index.html` for any unknown paths: + +```diff + app.use(express.static('./build')); + +-app.get('/', function (req, res) { ++app.get('/*', function (req, res) { + res.sendFile(path.join(__dirname, './build', 'index.html')); + }); +``` + +Now requests to `/todos/42` will be handled correctly both in development and in production. + +### Building for Relative Paths By default, Create React App produces a build assuming your app is hosted at the server root.<br> To override this, specify the `homepage` in your `package.json`, for example: @@ -874,51 +1055,151 @@ To override this, specify the `homepage` in your `package.json`, for example: This will let Create React App correctly infer the root path to use in the generated HTML file. + +### Firebase + +Install the Firebase CLI if you haven't already by running `npm install -g firebase-tools`. Sign up for a [Firebase account](https://console.firebase.google.com/) and create a new project. Run `firebase login` and login with your previous created Firebase account. + +Then run the `firebase init` command from your project's root. You need to choose the **Hosting: Configure and deploy Firebase Hosting sites** and choose the Firebase project you created in the previous step. You will need to agree with `database.rules.json` being created, choose `build` as the public directory, and also agree to **Configure as a single-page app** by replying with `y`. + +```sh + === Project Setup + + First, let's associate this project directory with a Firebase project. + You can create multiple project aliases by running firebase use --add, + but for now we'll just set up a default project. + + ? What Firebase project do you want to associate as default? Example app (example-app-fd690) + + === Database Setup + + Firebase Realtime Database Rules allow you to define how your data should be + structured and when your data can be read from and written to. + + ? What file should be used for Database Rules? database.rules.json + ✔ Database Rules for example-app-fd690 have been downloaded to database.rules.json. + Future modifications to database.rules.json will update Database Rules when you run + firebase deploy. + + === Hosting Setup + + Your public directory is the folder (relative to your project directory) that + will contain Hosting assets to uploaded with firebase deploy. If you + have a build process for your assets, use your build's output directory. + + ? What do you want to use as your public directory? build + ? Configure as a single-page app (rewrite all urls to /index.html)? Yes + ✔ Wrote build/index.html + + i Writing configuration info to firebase.json... + i Writing project information to .firebaserc... + + ✔ Firebase initialization complete! +``` + +Now, after you create a production build with `npm run build`, you can deploy it by running `firebase deploy`. + +```sh + === Deploying to 'example-app-fd690'... + + i deploying database, hosting + ✔ database: rules ready to deploy. + i hosting: preparing build directory for upload... + Uploading: [============================== ] 75%✔ hosting: build folder uploaded successfully + ✔ hosting: 8 files uploaded successfully + i starting release process (may take several minutes)... + + ✔ Deploy complete! + + Project Console: https://console.firebase.google.com/project/example-app-fd690/overview + Hosting URL: https://example-app-fd690.firebaseapp.com +``` + +For more information see [Add Firebase to your JavaScript Project](https://firebase.google.com/docs/web/setup). + ### GitHub Pages >Note: this feature is available with `react-scripts@0.2.0` and higher. +#### Step 1: Add `homepage` to `package.json` + +**The step below is important!**<br> +**If you skip it, your app will not deploy correctly.** + Open your `package.json` and add a `homepage` field: ```js - "homepage": "http://myusername.github.io/my-app", + "homepage": "https://myusername.github.io/my-app", ``` -**The above step is important!**<br> Create React App uses the `homepage` field to determine the root URL in the built HTML file. -Now, whenever you run `npm run build`, you will see a cheat sheet with instructions on how to deploy to GitHub pages. +#### Step 2: Install `gh-pages` and add `deploy` to `scripts` in `package.json` + +Now, whenever you run `npm run build`, you will see a cheat sheet with instructions on how to deploy to GitHub Pages. -To publish it at [http://myusername.github.io/my-app](http://myusername.github.io/my-app), run: +To publish it at [https://myusername.github.io/my-app](https://myusername.github.io/my-app), run: ```sh npm install --save-dev gh-pages ``` -Add the following script in your `package.json`: +Add the following scripts in your `package.json`: ```js // ... "scripts": { // ... + "predeploy": "npm run build", "deploy": "gh-pages -d build" } ``` +The `predeploy` script will run automatically before `deploy` is run. + +#### Step 3: Deploy the site by running `npm run deploy` + Then run: ```sh npm run deploy ``` -Note that GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions: +#### Step 4: Ensure your project's settings use `gh-pages` + +Finally, make sure **GitHub Pages** option in your GitHub project settings is set to use the `gh-pages` branch: + +<img src="http://i.imgur.com/HUjEr9l.png" width="500" alt="gh-pages branch setting"> + +#### Step 5: Optionally, configure the domain + +You can configure a custom domain with GitHub Pages by adding a `CNAME` file to the `public/` folder. + +#### Notes on client-side routing + +GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions: + * You could switch from using HTML5 history API to routing with hashes. If you use React Router, you can switch to `hashHistory` for this effect, but the URL will be longer and more verbose (for example, `http://user.github.io/todomvc/#/todos/42?_k=yknaj`). [Read more](https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md#histories) about different history implementations in React Router. * Alternatively, you can use a trick to teach GitHub Pages to handle 404 by redirecting to your `index.html` page with a special redirect parameter. You would need to add a `404.html` file with the redirection code to the `build` folder before deploying your project, and you’ll need to add code handling the redirect parameter to `index.html`. You can find a detailed explanation of this technique [in this guide](https://github.com/rafrex/spa-github-pages). ### Heroku Use the [Heroku Buildpack for Create React App](https://github.com/mars/create-react-app-buildpack).<br> -You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). +You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). + +#### Resolving "Module not found: Error: Cannot resolve 'file' or 'directory'" + +Sometimes `npm run build` works locally but fails during deploy via Heroku with an error like this: + +``` +remote: Failed to create a production build. Reason: +remote: Module not found: Error: Cannot resolve 'file' or 'directory' +MyDirectory in /tmp/build_1234/src +``` + +This means you need to ensure that the lettercase of the file or directory you `import` matches the one you see on your filesystem or on GitHub. + +This is important because Linux (the operating system used by Heroku) is case sensitive. So `MyDirectory` and `mydirectory` are two distinct directories and thus, even though the project builds locally, the difference in case breaks the `import` statements on Heroku remotes. ### Modulus @@ -957,6 +1238,10 @@ When you build the project, Create React App will place the `public` folder cont See [this example](https://github.com/xkawi/create-react-app-now) for a zero-configuration single-command deployment with [now](https://zeit.co/now). +### S3 and CloudFront + +See this [blog post](https://medium.com/@omgwtfmarc/deploying-create-react-app-to-s3-or-cloudfront-48dae4ce0af) on how to deploy your React app to Amazon Web Services [S3](https://aws.amazon.com/s3) and [CloudFront](https://aws.amazon.com/cloudfront/). + ### Surge Install the Surge CLI if you haven't already by running `npm install -g surge`. Run the `surge` command and log in you or create a new account. You just need to specify the *build* folder and your custom domain, and you are done. @@ -978,6 +1263,41 @@ Install the Surge CLI if you haven't already by running `npm install -g surge`. Note that in order to support routers that use HTML5 `pushState` API, you may want to rename the `index.html` in your build folder to `200.html` before deploying to Surge. This [ensures that every URL falls back to that file](https://surge.sh/help/adding-a-200-page-for-client-side-routing). +## Troubleshooting + +### `npm test` hangs on macOS Sierra + +If you run `npm test` and the console gets stuck after printing `react-scripts test --env=jsdom` to the console there might be a problem with your [Watchman](https://facebook.github.io/watchman/) installation as described in [facebookincubator/create-react-app#713](https://github.com/facebookincubator/create-react-app/issues/713). + +We recommend deleting `node_modules` in your project and running `npm install` (or `yarn` if you use it) first. If it doesn't help, you can try one of the numerous workarounds mentioned in these issues: + +* [facebook/jest#1767](https://github.com/facebook/jest/issues/1767) +* [facebook/watchman#358](https://github.com/facebook/watchman/issues/358) +* [ember-cli/ember-cli#6259](https://github.com/ember-cli/ember-cli/issues/6259) + +It is reported that installing Watchman 4.7.0 or newer fixes the issue. If you use [Homebrew](http://brew.sh/), you can run these commands to update it: + +``` +watchman shutdown-server +brew update +brew reinstall watchman +``` + +You can find [other installation methods](https://facebook.github.io/watchman/docs/install.html#build-install) on the Watchman documentation page. + +If this still doesn't help, try running `launchctl unload -F ~/Library/LaunchAgents/com.github.facebook.watchman.plist`. + +There are also reports that *uninstalling* Watchman fixes the issue. So if nothing else helps, remove it from your system and try again. + +### `npm run build` silently fails + +It is reported that `npm run build` can fail on machines with no swap space, which is common in cloud environments. If [the symptoms are matching](https://github.com/facebookincubator/create-react-app/issues/1133#issuecomment-264612171), consider adding some swap space to the machine you’re building on, or build the project locally. + +### `npm run build` fails on Heroku + +This may be a problem with case sensitive filenames. +Please refer to [this section](#resolving-module-not-found-error-cannot-resolve-file-or-directory). + ## Something Missing? If you have ideas for more “How To” recipes that should be on this page, [let us know](https://github.com/facebookincubator/create-react-app/issues) or [contribute some!](https://github.com/facebookincubator/create-react-app/edit/master/packages/react-scripts/template/README.md) diff --git a/packages/react-scripts/template/gitignore b/packages/react-scripts/template/gitignore index 6c96c5cff12..45edb3c7d80 100644 --- a/packages/react-scripts/template/gitignore +++ b/packages/react-scripts/template/gitignore @@ -1,13 +1,13 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. # dependencies -node_modules +/node_modules # testing -coverage +/coverage # production -build +/build # misc .DS_Store diff --git a/packages/react-scripts/utils/createJestConfig.js b/packages/react-scripts/utils/createJestConfig.js index 39c864ab8f4..f1c67c018f1 100644 --- a/packages/react-scripts/utils/createJestConfig.js +++ b/packages/react-scripts/utils/createJestConfig.js @@ -9,32 +9,41 @@ // Note: this file does not exist after ejecting. -const pathExists = require('path-exists'); +const fs = require('fs'); const paths = require('../config/paths'); module.exports = (resolve, rootDir, isEjecting) => { // Use this instead of `paths.testsSetup` to avoid putting // an absolute filename into configuration after ejecting. - const setupTestsFile = pathExists.sync(paths.testsSetup) ? '<rootDir>/src/setupTests.js' : undefined; + const setupTestsFile = fs.existsSync(paths.testsSetup) ? '<rootDir>/src/setupTests.js' : undefined; + // TODO: I don't know if it's safe or not to just use / as path separator + // in Jest configs. We need help from somebody with Windows to determine this. const config = { - moduleFileExtensions: ['jsx', 'js', 'json'], - moduleNameMapper: { - '^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': resolve('config/jest/FileStub.js'), - '^.+\\.css$': resolve('config/jest/CSSStub.js') - }, + collectCoverageFrom: ['src/**/*.{js,jsx}'], setupFiles: [resolve('config/polyfills.js')], setupTestFrameworkScriptFile: setupTestsFile, - testPathIgnorePatterns: ['<rootDir>/(build|docs|node_modules)/'], + testPathIgnorePatterns: [ + '<rootDir>[/\\\\](build|docs|node_modules|scripts)[/\\\\]' + ], testEnvironment: 'node', + testURL: 'http://localhost', + transform: { + '^.+\\.(js|jsx)$': isEjecting ? + '<rootDir>/node_modules/babel-jest' + : resolve('config/jest/babelTransform.js'), + '^.+\\.css$': resolve('config/jest/cssTransform.js'), + '^(?!.*\\.(js|jsx|css|json)$)': resolve('config/jest/fileTransform.js'), + }, + transformIgnorePatterns: [ + '[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$' + ], + moduleNameMapper: { + '^react-native$': 'react-native-web' + } }; if (rootDir) { config.rootDir = rootDir; } - if (!isEjecting) { - // This is unnecessary after ejecting because Jest - // will just use .babelrc in the project folder. - config.scriptPreprocessor = resolve('config/jest/transform.js'); - } return config; }; diff --git a/tasks/cra.sh b/tasks/cra.sh index 0271536d706..f63fd464b0c 100755 --- a/tasks/cra.sh +++ b/tasks/cra.sh @@ -59,9 +59,9 @@ cd packages/react-scripts # Save package.json because we're going to touch it cp package.json package.json.orig -# Like bundle-deps, this script modifies packages/react-scripts/package.json, -# copying own dependencies (those in the `packages` dir) to bundledDependencies -node $root_path/tasks/bundle-own-deps.js +# Replace own dependencies (those in the `packages` dir) with the local paths +# of those packages. +node $root_path/tasks/replace-own-deps.js # Finally, pack react-scripts scripts_path=$root_path/packages/react-scripts/`npm pack` @@ -75,6 +75,9 @@ mv package.json.orig package.json # Now that we have packed them, call the global CLI. # ****************************************************************************** +# If Yarn is installed, clean its cache because it may have cached react-scripts +yarn cache clean || true + # Go back to the root directory and run the command from here cd $root_path node packages/create-react-app/index.js --scripts-version=$scripts_path "$@" diff --git a/tasks/e2e.sh b/tasks/e2e.sh index 88e1fdf4e20..27cd0ae45f6 100755 --- a/tasks/e2e.sh +++ b/tasks/e2e.sh @@ -14,6 +14,11 @@ # Start in tasks/ even if run from root directory cd "$(dirname "$0")" +# CLI and app temporary locations +# http://unix.stackexchange.com/a/84980 +temp_cli_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_cli_path'` +temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` + function cleanup { echo 'Cleaning up.' cd $root_path @@ -55,6 +60,21 @@ root_path=$PWD npm install +# If the node version is < 4, the script should just give an error. +if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ] +then + cd $temp_app_path + err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''` + [[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1 +fi + +if [ "$USE_YARN" = "yes" ] +then + # Install Yarn so that the test can use it to install packages. + npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed. + yarn cache clean +fi + # Lint own code ./node_modules/.bin/eslint --ignore-path .gitignore ./ @@ -91,25 +111,29 @@ cli_path=$PWD/`npm pack` # Go to react-scripts cd $root_path/packages/react-scripts -# Like bundle-deps, this script modifies packages/react-scripts/package.json, -# copying own dependencies (those in the `packages` dir) to bundledDependencies -node $root_path/tasks/bundle-own-deps.js +# Save package.json because we're going to touch it +cp package.json package.json.orig + +# Replace own dependencies (those in the `packages` dir) with the local paths +# of those packages. +node $root_path/tasks/replace-own-deps.js # Finally, pack react-scripts scripts_path=$root_path/packages/react-scripts/`npm pack` +# Restore package.json +rm package.json +mv package.json.orig package.json + # ****************************************************************************** # Now that we have packed them, create a clean app folder and install them. # ****************************************************************************** # Install the CLI in a temporary location -# http://unix.stackexchange.com/a/84980 -temp_cli_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_cli_path'` cd $temp_cli_path npm install $cli_path # Install the app in a temporary location -temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` cd $temp_app_path create_react_app --scripts-version=$scripts_path test-app @@ -171,7 +195,6 @@ npm test -- --watch=no # Test the server npm start -- --smoke-test - # ****************************************************************************** # Test --scripts-version with a version number # ****************************************************************************** @@ -207,5 +230,31 @@ cd test-app-fork # Check corresponding scripts version is installed. test -e node_modules/react-scripts-fork +# ****************************************************************************** +# Test nested folder path as the project name +# ****************************************************************************** + +#Testing a path that exists +cd $temp_app_path +mkdir test-app-nested-paths-t1 +cd test-app-nested-paths-t1 +mkdir -p test-app-nested-paths-t1/aa/bb/cc/dd +create_react_app test-app-nested-paths-t1/aa/bb/cc/dd +cd test-app-nested-paths-t1/aa/bb/cc/dd +npm start -- --smoke-test + +#Testing a path that does not exist +cd $temp_app_path +create_react_app test-app-nested-paths-t2/aa/bb/cc/dd +cd test-app-nested-paths-t2/aa/bb/cc/dd +npm start -- --smoke-test + +#Testing a path that is half exists +cd $temp_app_path +mkdir -p test-app-nested-paths-t3/aa +create_react_app test-app-nested-paths-t3/aa/bb/cc/dd +cd test-app-nested-paths-t3/aa/bb/cc/dd +npm start -- --smoke-test + # Cleanup cleanup diff --git a/tasks/release.sh b/tasks/release.sh index 14b5cb77b18..1520a5f0785 100755 --- a/tasks/release.sh +++ b/tasks/release.sh @@ -39,23 +39,6 @@ if [ -n "$(git status --porcelain)" ]; then exit 1; fi -# Update deps -rm -rf node_modules -rm -rf ~/.npm -npm cache clear -npm install - -cd packages/react-scripts -# Force dedupe -npm dedupe - -# Don't bundle fsevents because it is optional and OS X-only -# Since it's in optionalDependencies, it will attempt install outside bundle -rm -rf node_modules/fsevents - -# This modifies package.json to copy all dependencies to bundledDependencies -node ./node_modules/.bin/bundle-deps - cd $root_path # Go! ./node_modules/.bin/lerna publish --independent "$@" diff --git a/tasks/bundle-own-deps.js b/tasks/replace-own-deps.js similarity index 66% rename from tasks/bundle-own-deps.js rename to tasks/replace-own-deps.js index 61408f1cc00..23a3f4a01fe 100755 --- a/tasks/bundle-own-deps.js +++ b/tasks/replace-own-deps.js @@ -9,8 +9,7 @@ */ 'use strict'; -// Like bundle-deps, this script modifies packages/react-scripts/package.json, -// copying own dependencies (those in the `packages` dir) to bundledDependencies +// Replaces internal dependencies in package.json with local package paths. const fs = require('fs'); const path = require('path'); @@ -19,10 +18,13 @@ const packagesDir = path.join(__dirname, '../packages'); const pkgFilename = path.join(packagesDir, 'react-scripts/package.json'); const data = require(pkgFilename); -data.bundledDependencies = fs.readdirSync(packagesDir) - .filter((name) => data.dependencies[name]); +fs.readdirSync(packagesDir).forEach((name) => { + if (data.dependencies[name]) { + data.dependencies[name] = 'file:' + path.join(packagesDir, name); + } +}) fs.writeFile(pkgFilename, JSON.stringify(data, null, 2), 'utf8', (err) => { if (err) throw err; - console.log('bundled ' + data.bundledDependencies.length + ' dependencies.'); + console.log('Replaced local dependencies.'); });