diff --git a/.eslintignore b/.eslintignore
index 4eaf46c6d7d..27c694cea55 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,3 +3,4 @@ build
my-app*
packages/react-scripts/template
packages/react-scripts/fixtures
+fixtures/
diff --git a/.eslintrc b/.eslintrc
index 44a504e4080..b642bbc5c14 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -7,7 +7,7 @@
"es6": true
},
"parserOptions": {
- "ecmaVersion": 6
+ "ecmaVersion": 2018
},
"rules": {
"no-console": "off",
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 4ef50efcac3..d2612759e2f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -85,19 +85,18 @@
### Environment
+ To help identify if a problem is specific to a platform, browser, or module version, information about your environment is required.
+ This enables the maintainers quickly reproduce the issue and give feedback.
-1. `node -v`:
-2. `npm -v`:
-3. `yarn --version` (if you use Yarn):
-4. `npm ls react-scripts` (if you haven’t ejected):
+ Run the following command in your React app's folder in terminal.
+ Note: The result is copied to your clipboard directly.
-Then, specify:
+ `npx create-react-app --info`
-1. Operating system:
-2. Browser and version (if relevant):
+ Paste the output of the command in the section below.
+-->
+(paste the output of the command here)
### Steps to Reproduce
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000000..8ff820d3bc0
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "trailingComma": "es5",
+ "singleQuote": true,
+ "semi": true
+}
diff --git a/.travis.yml b/.travis.yml
index f27e0e9a2b8..d5fdc156413 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,28 +3,33 @@ dist: trusty
language: node_js
node_js:
- 8
- - 9
+ - 10
cache:
+ yarn: true
directories:
- - node_modules
- - packages/create-react-app/node_modules
- - packages/react-scripts/node_modules
+ - .npm
+before_install:
+ - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --nightly
+ - export PATH="$HOME/.yarn/bin:$PATH"
install: true
script:
- - 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
- - 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
- - 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi'
- - 'if [ $TEST_SUITE = "old-node" ]; then tasks/e2e-old-node.sh; fi'
- - 'if [ $TEST_SUITE = "monorepos" ]; then tasks/e2e-monorepos.sh; fi'
+ - 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi'
+ - 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi'
+ - 'if [ $TEST_SUITE = "kitchensink" ]; then tasks/e2e-kitchensink.sh; fi'
+ - 'if [ $TEST_SUITE = "kitchensink-eject" ]; then tasks/e2e-kitchensink-eject.sh; fi'
+ - 'if [ $TEST_SUITE = "old-node" ]; then tasks/e2e-old-node.sh; fi'
+ - 'if [ $TEST_SUITE = "behavior" ]; then tasks/e2e-behavior.sh; fi'
env:
matrix:
- TEST_SUITE=simple
- TEST_SUITE=installs
- TEST_SUITE=kitchensink
- - TEST_SUITE=monorepos
+ - TEST_SUITE=kitchensink-eject
+ - TEST_SUITE=behavior
matrix:
include:
- - node_js: 0.10
+ - os: osx
+ node_js: 8
+ env: TEST_SUITE=behavior
+ - node_js: 4
env: TEST_SUITE=old-node
- - node_js: 6
- env: TEST_SUITE=kitchensink
diff --git a/.yarnrc b/.yarnrc
deleted file mode 100644
index 07e44a9a5b2..00000000000
--- a/.yarnrc
+++ /dev/null
@@ -1,3 +0,0 @@
---install.no-lockfile true
---install.check-files true
---add.no-lockfile true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c63d42f570..5b2c643d3e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,35 @@
+## 2.0.2 (October 1, 2018)
+
+TODO (work in progress)
+
+## 1.1.5 (August 24, 2018)
+
+* `react-scripts`
+
+ * Update the `webpack-dev-server` dependency
+
+* `react-dev-utils`
+
+ * [#4866](https://github.com/facebook/create-react-app/pull/4866) Fix a Windows-only vulnerability (`CVE-2018-6342`) in the development server ([@acdlite](https://github.com/acdlite))
+ * Update the `sockjs-client` dependency
+
+#### Committers: 1
+- Andrew Clark ([acdlite](https://github.com/acdlite))
+
+### Migrating from 1.1.4 to 1.1.5
+
+Inside any created project that has not been ejected, run:
+
+```
+npm install --save --save-exact react-scripts@1.1.5
+```
+
+or
+
+```
+yarn add --exact react-scripts@1.1.5
+```
+
## 1.1.4 (April 3, 2018)
#### :bug: Bug Fix
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 55203be746a..0fb245803d8 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,3 +1,3 @@
# Code of Conduct
-Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct) so that you can understand what actions will and will not be tolerated.
+Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.fb.com/codeofconduct/) so that you can understand what actions will and will not be tolerated.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e69f729fd27..a51391c44bc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -57,7 +57,7 @@ packages/
### Package Descriptions
#### [babel-preset-react-app](https://github.com/facebook/create-react-app/tree/master/packages/babel-preset-react-app)
This package is a babel preset intended to be used with `react-scripts`.
-It targets platforms that React is designed to support (IE 9+) and enables experimental features used heavily at Facebook.
+It targets platforms that React is designed to support (IE 11+) and enables experimental features used heavily at Facebook.
This package is enabled by default for all `create-react-app` scaffolded applications.
#### [create-react-app](https://github.com/facebook/create-react-app/tree/master/packages/create-react-app)
The global CLI command code can be found in this directory, and shouldn't often be changed. It should run on Node 0.10+.
diff --git a/README.md b/README.md
index 655aeff4955..c4390b59748 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,9 @@ Just create a project, and you’re good to go.
**You’ll need to have Node >= 6 on your local development machine** (but it’s not required on the server). You can use [nvm](https://github.com/creationix/nvm#installation) (macOS/Linux) or [nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows) to easily switch Node versions between different projects.
-To create a new app, run a single command:
+To create a new app, you may choose one of the following methods:
+
+### npx
```sh
npx create-react-app my-app
@@ -44,6 +46,20 @@ npx create-react-app my-app
*([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher, see [instructions for older npm versions](https://gist.github.com/gaearon/4064d3c23a77c74a3614c498a8bb1c5f))*
+### npm
+
+```sh
+npm init react-app my-app
+```
+*`npm init ` is available in npm 6+*
+
+### Yarn
+
+```sh
+yarn create react-app my-app
+```
+*`yarn create` is available in Yarn 0.25+*
+
It will create a directory called `my-app` inside the current folder.
Inside that directory, it will generate the initial project structure and install the transitive dependencies:
@@ -54,16 +70,16 @@ my-app
├── package.json
├── .gitignore
├── public
-│ └── favicon.ico
-│ └── index.html
+│ ├── favicon.ico
+│ ├── index.html
│ └── manifest.json
└── src
- └── App.css
- └── App.js
- └── App.test.js
- └── index.css
- └── index.js
- └── logo.svg
+ ├── App.css
+ ├── App.js
+ ├── App.test.js
+ ├── index.css
+ ├── index.js
+ ├── logo.svg
└── registerServiceWorker.js
```
@@ -101,7 +117,6 @@ Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
-By default, it also [includes a service worker](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) so that your app loads from local cache on future visits.
Your app is ready to be deployed.
@@ -147,7 +162,7 @@ The [User Guide](https://github.com/facebook/create-react-app/blob/master/packag
- [Analyzing the Bundle Size](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#analyzing-the-bundle-size)
- [Deployment](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment)
- [Advanced Configuration](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration)
-- [Troubleshooting](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting)
+- [Troubleshooting](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting-1)
A copy of the user guide will be created as `README.md` in your project folder.
@@ -173,7 +188,7 @@ Your environment will have everything you need to build a modern single-page Rea
* A fast interactive unit test runner with built-in support for coverage reporting.
* A live development server that warns about common mistakes.
* A build script to bundle JS, CSS, and images for production, with hashes and sourcemaps.
-* An offline-first [service worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) and a [web app manifest](https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/), meeting all the [Progressive Web App](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) criteria.
+* An offline-first [service worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) and a [web app manifest](https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/), meeting all the [Progressive Web App](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) criteria. (*Note: Using the service worker is opt-in as of `react-scripts@2.0.0` and higher*)
* Hassle-free updates for the above tools with a single dependency.
Check out [this guide](https://github.com/nitishdayal/cra_closer_look) for an overview of how these tools fit together.
@@ -202,6 +217,8 @@ Here’s a few common cases where you might want to try something else:
* If you want to use **TypeScript**, consider using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript).
+* If you want to use **Parcel** instead of **Webpack** as your bundler, consider using [create-react-app-parcel](https://github.com/sw-yx/create-react-app-parcel).
+
* Finally, if you need **more customization**, check out [Neutrino](https://neutrino.js.org/) and its [React preset](https://neutrino.js.org/packages/react/).
All of the above tools can work with little to no configuration.
diff --git a/appveyor.cleanup-cache.txt b/appveyor.cleanup-cache.txt
index 200d97c3aac..d48a91fdf35 100644
--- a/appveyor.cleanup-cache.txt
+++ b/appveyor.cleanup-cache.txt
@@ -2,5 +2,3 @@ Edit this file to trigger a cache rebuild.
http://help.appveyor.com/discussions/questions/1310-delete-cache
----
-
-bump
diff --git a/appveyor.yml b/appveyor.yml
index db1d640caa1..5f5143167d6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,37 +1,44 @@
image: Visual Studio 2017
environment:
+ APPVEYOR_SAVE_CACHE_ON_ERROR: true
+ APPVEYOR_BUILD_WORKER_CLOUD: 'GCE'
matrix:
+ - nodejs_version: 10
+ test_suite: 'simple'
+ - nodejs_version: 10
+ test_suite: 'installs'
+ - nodejs_version: 10
+ test_suite: 'kitchensink'
+ - nodejs_version: 10
+ test_suite: 'kitchensink-eject'
- nodejs_version: 8
- test_suite: "simple"
+ test_suite: 'simple'
- nodejs_version: 8
- test_suite: "installs"
+ test_suite: 'installs'
- nodejs_version: 8
- test_suite: "kitchensink"
+ test_suite: 'kitchensink'
- nodejs_version: 8
- test_suite: "monorepos"
- - nodejs_version: 6
- test_suite: "simple"
- - nodejs_version: 6
- test_suite: "installs"
- - nodejs_version: 6
- test_suite: "kitchensink"
- - nodejs_version: 6
- test_suite: "monorepos"
+ test_suite: 'kitchensink-eject'
cache:
- - node_modules -> appveyor.cleanup-cache.txt
- - packages\react-scripts\node_modules -> appveyor.cleanup-cache.txt
+ - '%APPDATA%\npm-cache -> appveyor.cleanup-cache.txt'
+ - '%LOCALAPPDATA%\Yarn\Cache -> appveyor.cleanup-cache.txt'
clone_depth: 50
matrix:
fast_finish: true
+ allow_failures:
+ - test_suite: 'installs'
platform:
- x64
install:
- ps: Install-Product node $env:nodejs_version $env:platform
+ - ps: |
+ (New-Object Net.WebClient).DownloadFile("https://nightly.yarnpkg.com/latest.msi", "$env:temp\yarn.msi")
+ cmd /c start /wait msiexec.exe /i $env:temp\yarn.msi /quiet /qn /norestart
build: off
@@ -42,4 +49,6 @@ skip_commits:
test_script:
- node --version
- npm --version
+ - yarn --version
+ - yarn cache dir
- bash tasks/e2e-%test_suite%.sh
diff --git a/fixtures/output/jest.config.js b/fixtures/output/jest.config.js
new file mode 100644
index 00000000000..fa718fa3ea3
--- /dev/null
+++ b/fixtures/output/jest.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ testEnvironment: 'node',
+ testMatch: ['**/*.test.js'],
+ setupTestFrameworkScriptFile: './setupOutputTests.js',
+};
diff --git a/fixtures/output/setupOutputTests.js b/fixtures/output/setupOutputTests.js
new file mode 100644
index 00000000000..b709406980f
--- /dev/null
+++ b/fixtures/output/setupOutputTests.js
@@ -0,0 +1,6 @@
+beforeAll(() => {
+ jest.setTimeout(1000 * 60 * 5);
+});
+beforeEach(() => {
+ jest.setTimeout(1000 * 60 * 5);
+});
diff --git a/fixtures/output/webpack-message-formatting/__snapshots__/index.test.js.snap b/fixtures/output/webpack-message-formatting/__snapshots__/index.test.js.snap
new file mode 100644
index 00000000000..6a510ef70b9
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/__snapshots__/index.test.js.snap
@@ -0,0 +1,177 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`webpack message formatting formats aliased unknown export 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Attempted import error: 'bar' is not exported from './AppUnknownExport' (imported as 'bar2').
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats babel syntax error 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Syntax error: Unterminated JSX contents (8:13)
+
+ 6 |
+ 7 |
+> 8 |
+ | ^
+ 9 | );
+ 10 | }
+ 11 | }
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats css syntax error 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/AppCss.css
+Syntax error: Unexpected } (3:2)
+
+ 1 | .App {
+ 2 | color: red;
+> 3 | }}
+ | ^
+ 4 |
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats eslint error 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+ Line 4: 'b' is not defined no-undef
+
+Search for the keywords to learn more about each error.
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats eslint warning 1`] = `
+Object {
+ "stderr": "",
+ "stdout": "Creating an optimized production build...
+Compiled with warnings.
+
+./src/App.js
+ Line 3: 'foo' is defined but never used no-unused-vars
+
+Search for the keywords to learn more about each warning.
+To ignore, add // eslint-disable-next-line to the line before.
+
+",
+}
+`;
+
+exports[`webpack message formatting formats file not found error 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Cannot find file './ThisFileSouldNotExist' in './src'.
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats missing package 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Cannot find module: 'unknown-package'. Make sure this package is installed.
+
+You can install this package by running: yarn add unknown-package.
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats no default export 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Attempted import error: './ExportNoDefault' does not contain a default export (imported as 'myImport').
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats out of scope error 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+You attempted to import ../OutOfScopeImport which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
+You can either move it inside src/, or add a symlink to it from project's node_modules/.
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting formats unknown export 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/App.js
+Attempted import error: 'bar' is not exported from './AppUnknownExport'.
+
+
+",
+ "stdout": "",
+}
+`;
+
+exports[`webpack message formatting helps when users tries to use sass 1`] = `
+Object {
+ "stderr": "Creating an optimized production build...
+Failed to compile.
+
+./src/AppSass.scss
+To import Sass files, you first need to install node-sass.
+Run \`npm install node-sass\` or \`yarn add node-sass\` inside your workspace.
+
+
+",
+ "stdout": "",
+}
+`;
diff --git a/fixtures/output/webpack-message-formatting/index.test.js b/fixtures/output/webpack-message-formatting/index.test.js
new file mode 100644
index 00000000000..475fb108536
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/index.test.js
@@ -0,0 +1,157 @@
+const {
+ bootstrap,
+ getOutputDevelopment,
+ getOutputProduction,
+} = require('../../utils');
+const fs = require('fs-extra');
+const path = require('path');
+const Semaphore = require('async-sema');
+const tempy = require('tempy');
+
+describe('webpack message formatting', () => {
+ const semaphore = new Semaphore(1, { capacity: Infinity });
+ let testDirectory;
+ beforeAll(async () => {
+ testDirectory = tempy.directory();
+ await bootstrap({ directory: testDirectory, template: __dirname });
+ });
+ beforeEach(async () => {
+ await semaphore.acquire();
+ });
+ afterEach(async () => {
+ fs.removeSync(path.join(testDirectory, 'src', 'App.js'));
+ semaphore.release();
+ });
+
+ it('formats babel syntax error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppBabel.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats css syntax error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppCss.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats unknown export', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppUnknownExport.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats aliased unknown export', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppAliasUnknownExport.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats no default export', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppNoDefault.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats missing package', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppMissingPackage.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats eslint warning', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppLintWarning.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ const sizeIndex = response.stdout.indexOf('File sizes after gzip');
+ if (sizeIndex !== -1) {
+ response.stdout = response.stdout.substring(0, sizeIndex);
+ }
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats eslint error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppLintError.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('helps when users tries to use sass', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppSass.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats file not found error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppUnknownFile.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+
+ it('formats case sensitive path error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppIncorrectCase.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputDevelopment({ directory: testDirectory });
+ if (process.platform === 'darwin') {
+ expect(response.stderr).toMatch(
+ `Cannot find file: 'export5.js' does not match the corresponding name on disk: './src/Export5.js'.`
+ );
+ } else {
+ expect(response.stderr).not.toEqual(''); // TODO: figure out how we can test this on Linux/Windows
+ // I believe getting this working requires we tap into enhanced-resolve
+ // pipeline, which is debt we don't want to take on right now.
+ }
+ });
+
+ it('formats out of scope error', async () => {
+ fs.copySync(
+ path.join(__dirname, 'src', 'AppOutOfScopeImport.js'),
+ path.join(testDirectory, 'src', 'App.js')
+ );
+
+ const response = await getOutputProduction({ directory: testDirectory });
+ expect(response).toMatchSnapshot();
+ });
+});
diff --git a/fixtures/output/webpack-message-formatting/package.json b/fixtures/output/webpack-message-formatting/package.json
new file mode 100644
index 00000000000..4fda3598df5
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/package.json
@@ -0,0 +1,9 @@
+{
+ "dependencies": {
+ "react": "latest",
+ "react-dom": "latest"
+ },
+ "browserslist": [
+ ">0.2%"
+ ]
+}
diff --git a/fixtures/output/webpack-message-formatting/public/index.html b/fixtures/output/webpack-message-formatting/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/output/webpack-message-formatting/src/AppAliasUnknownExport.js b/fixtures/output/webpack-message-formatting/src/AppAliasUnknownExport.js
new file mode 100644
index 00000000000..df2716efc78
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppAliasUnknownExport.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react';
+import { bar as bar2 } from './AppUnknownExport';
+
+class App extends Component {
+ componentDidMount() {
+ bar2();
+ }
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppBabel.js b/fixtures/output/webpack-message-formatting/src/AppBabel.js
new file mode 100644
index 00000000000..b6799d38e5f
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppBabel.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react';
+
+class App extends Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppCss.css b/fixtures/output/webpack-message-formatting/src/AppCss.css
new file mode 100644
index 00000000000..530380750ec
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppCss.css
@@ -0,0 +1,3 @@
+.App {
+ color: red;
+}}
diff --git a/fixtures/output/webpack-message-formatting/src/AppCss.js b/fixtures/output/webpack-message-formatting/src/AppCss.js
new file mode 100644
index 00000000000..af04f1c60b5
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppCss.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import './AppCss.css';
+
+class App extends Component {
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppIncorrectCase.js b/fixtures/output/webpack-message-formatting/src/AppIncorrectCase.js
new file mode 100644
index 00000000000..40f69d43938
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppIncorrectCase.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import five from './export5';
+
+class App extends Component {
+ render() {
+ return {five}
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppLintError.js b/fixtures/output/webpack-message-formatting/src/AppLintError.js
new file mode 100644
index 00000000000..6015c397d47
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppLintError.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react';
+
+function foo() {
+ const a = b;
+}
+
+class App extends Component {
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppLintWarning.js b/fixtures/output/webpack-message-formatting/src/AppLintWarning.js
new file mode 100644
index 00000000000..0918e42b739
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppLintWarning.js
@@ -0,0 +1,11 @@
+import React, { Component } from 'react';
+
+function foo() {}
+
+class App extends Component {
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppMissingPackage.js b/fixtures/output/webpack-message-formatting/src/AppMissingPackage.js
new file mode 100644
index 00000000000..5e62c967430
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppMissingPackage.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react';
+import { bar } from 'unknown-package';
+
+class App extends Component {
+ componentDidMount() {
+ bar();
+ }
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppNoDefault.js b/fixtures/output/webpack-message-formatting/src/AppNoDefault.js
new file mode 100644
index 00000000000..9087eed49d0
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppNoDefault.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import myImport from './ExportNoDefault';
+
+class App extends Component {
+ render() {
+ return {myImport}
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppOutOfScopeImport.js b/fixtures/output/webpack-message-formatting/src/AppOutOfScopeImport.js
new file mode 100644
index 00000000000..a8717e23082
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppOutOfScopeImport.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import myImport from '../OutOfScopeImport';
+
+class App extends Component {
+ render() {
+ return {myImport}
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppSass.js b/fixtures/output/webpack-message-formatting/src/AppSass.js
new file mode 100644
index 00000000000..20ac0e211a5
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppSass.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import './AppSass.scss';
+
+class App extends Component {
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppSass.scss b/fixtures/output/webpack-message-formatting/src/AppSass.scss
new file mode 100644
index 00000000000..724638c3a7b
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppSass.scss
@@ -0,0 +1,3 @@
+.App {
+ color: red;
+}
diff --git a/fixtures/output/webpack-message-formatting/src/AppUnknownExport.js b/fixtures/output/webpack-message-formatting/src/AppUnknownExport.js
new file mode 100644
index 00000000000..482a545240b
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppUnknownExport.js
@@ -0,0 +1,13 @@
+import React, { Component } from 'react';
+import { bar } from './AppUnknownExport';
+
+class App extends Component {
+ componentDidMount() {
+ bar();
+ }
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/AppUnknownFile.js b/fixtures/output/webpack-message-formatting/src/AppUnknownFile.js
new file mode 100644
index 00000000000..d3b2ce26730
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/AppUnknownFile.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import DefaultExport from './ThisFileSouldNotExist';
+
+class App extends Component {
+ render() {
+ return
;
+ }
+}
+
+export default App;
diff --git a/fixtures/output/webpack-message-formatting/src/Export5.js b/fixtures/output/webpack-message-formatting/src/Export5.js
new file mode 100644
index 00000000000..ba35c21b821
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/Export5.js
@@ -0,0 +1 @@
+export default 5;
diff --git a/fixtures/output/webpack-message-formatting/src/ExportNoDefault.js b/fixtures/output/webpack-message-formatting/src/ExportNoDefault.js
new file mode 100644
index 00000000000..1608f67ee7a
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/ExportNoDefault.js
@@ -0,0 +1 @@
+export const six = 6;
diff --git a/fixtures/output/webpack-message-formatting/src/FooExport.js b/fixtures/output/webpack-message-formatting/src/FooExport.js
new file mode 100644
index 00000000000..acf7c881810
--- /dev/null
+++ b/fixtures/output/webpack-message-formatting/src/FooExport.js
@@ -0,0 +1,3 @@
+export function foo() {
+ console.log('bar');
+}
diff --git a/packages/react-scripts/fixtures/monorepos/packages/cra-app1/src/index.js b/fixtures/output/webpack-message-formatting/src/index.js
similarity index 86%
rename from packages/react-scripts/fixtures/monorepos/packages/cra-app1/src/index.js
rename to fixtures/output/webpack-message-formatting/src/index.js
index 395b74997b2..b597a44232c 100644
--- a/packages/react-scripts/fixtures/monorepos/packages/cra-app1/src/index.js
+++ b/fixtures/output/webpack-message-formatting/src/index.js
@@ -1,6 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import './index.css';
import App from './App';
ReactDOM.render( , document.getElementById('root'));
diff --git a/fixtures/smoke/boostrap-sass/index.test.js b/fixtures/smoke/boostrap-sass/index.test.js
new file mode 100644
index 00000000000..44f3a6c9d13
--- /dev/null
+++ b/fixtures/smoke/boostrap-sass/index.test.js
@@ -0,0 +1,17 @@
+const {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+} = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('bootstrap sass', () => {
+ it('builds in development', async () => {
+ await isSuccessfulDevelopment({ directory: global.testDirectory });
+ });
+ it('builds in production', async () => {
+ await isSuccessfulProduction({ directory: global.testDirectory });
+ });
+});
diff --git a/fixtures/smoke/boostrap-sass/package.json b/fixtures/smoke/boostrap-sass/package.json
new file mode 100644
index 00000000000..adb19270b6b
--- /dev/null
+++ b/fixtures/smoke/boostrap-sass/package.json
@@ -0,0 +1,8 @@
+{
+ "dependencies": {
+ "bootstrap": "4.x",
+ "node-sass": "4.x",
+ "react": "latest",
+ "react-dom": "latest"
+ }
+}
diff --git a/fixtures/smoke/boostrap-sass/public/index.html b/fixtures/smoke/boostrap-sass/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/boostrap-sass/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/boostrap-sass/src/index.js b/fixtures/smoke/boostrap-sass/src/index.js
new file mode 100644
index 00000000000..44f52927b5a
--- /dev/null
+++ b/fixtures/smoke/boostrap-sass/src/index.js
@@ -0,0 +1,5 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.sass';
+
+ReactDOM.render(
, document.getElementById('root'));
diff --git a/fixtures/smoke/boostrap-sass/src/index.sass b/fixtures/smoke/boostrap-sass/src/index.sass
new file mode 100644
index 00000000000..48fa0ce0d6e
--- /dev/null
+++ b/fixtures/smoke/boostrap-sass/src/index.sass
@@ -0,0 +1 @@
+@import "~bootstrap/scss/bootstrap.scss";
diff --git a/fixtures/smoke/builds-with-multiple-runtimes/index.test.js b/fixtures/smoke/builds-with-multiple-runtimes/index.test.js
new file mode 100644
index 00000000000..990ea7b357c
--- /dev/null
+++ b/fixtures/smoke/builds-with-multiple-runtimes/index.test.js
@@ -0,0 +1,17 @@
+const {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+} = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('builds-with-multiple-runtimes', () => {
+ it('builds in development', async () => {
+ await isSuccessfulDevelopment({ directory: global.testDirectory });
+ });
+ it('builds in production', async () => {
+ await isSuccessfulProduction({ directory: global.testDirectory });
+ });
+});
diff --git a/fixtures/smoke/builds-with-multiple-runtimes/package.json b/fixtures/smoke/builds-with-multiple-runtimes/package.json
new file mode 100644
index 00000000000..b2792516c6e
--- /dev/null
+++ b/fixtures/smoke/builds-with-multiple-runtimes/package.json
@@ -0,0 +1,8 @@
+{
+ "dependencies": {
+ "dva": "2.4.0",
+ "ky": "0.3.0",
+ "react": "latest",
+ "react-dom": "latest"
+ }
+}
diff --git a/fixtures/smoke/builds-with-multiple-runtimes/public/index.html b/fixtures/smoke/builds-with-multiple-runtimes/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/builds-with-multiple-runtimes/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/builds-with-multiple-runtimes/src/index.js b/fixtures/smoke/builds-with-multiple-runtimes/src/index.js
new file mode 100644
index 00000000000..b0603469a80
--- /dev/null
+++ b/fixtures/smoke/builds-with-multiple-runtimes/src/index.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import dva from 'dva';
+import createHistory from 'history/createHashHistory';
+import ky from 'ky';
+
+const app = dva({ history: createHistory() });
+app.router(() => {
+ ky.get('https://canihazip.com/s')
+ .then(r => r.text())
+ .then(console.log, console.error)
+ .then(() => console.log('ok'));
+ return Test
;
+});
+app.start('#root');
diff --git a/fixtures/smoke/graphql-with-mjs/index.test.js b/fixtures/smoke/graphql-with-mjs/index.test.js
new file mode 100644
index 00000000000..1f1d2bd078a
--- /dev/null
+++ b/fixtures/smoke/graphql-with-mjs/index.test.js
@@ -0,0 +1,17 @@
+const {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+} = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('graphql with mjs entrypoint', () => {
+ it('builds in development', async () => {
+ await isSuccessfulDevelopment({ directory: global.testDirectory });
+ });
+ it('builds in production', async () => {
+ await isSuccessfulProduction({ directory: global.testDirectory });
+ });
+});
diff --git a/fixtures/smoke/graphql-with-mjs/package.json b/fixtures/smoke/graphql-with-mjs/package.json
new file mode 100644
index 00000000000..c97e6b8d013
--- /dev/null
+++ b/fixtures/smoke/graphql-with-mjs/package.json
@@ -0,0 +1,9 @@
+{
+ "dependencies": {
+ "apollo-boost": "0.1.16",
+ "graphql": "14.0.2",
+ "react-apollo": "2.2.1",
+ "react": "latest",
+ "react-dom": "latest"
+ }
+}
diff --git a/fixtures/smoke/graphql-with-mjs/public/index.html b/fixtures/smoke/graphql-with-mjs/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/graphql-with-mjs/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/graphql-with-mjs/src/App.js b/fixtures/smoke/graphql-with-mjs/src/App.js
new file mode 100644
index 00000000000..1ba9891424a
--- /dev/null
+++ b/fixtures/smoke/graphql-with-mjs/src/App.js
@@ -0,0 +1,20 @@
+import React, { Component } from 'react';
+
+import ApolloClient from 'apollo-boost';
+import { ApolloProvider } from 'react-apollo';
+
+const client = new ApolloClient({
+ uri: '/whatever',
+});
+
+class App extends Component {
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+export default App;
diff --git a/fixtures/smoke/graphql-with-mjs/src/index.js b/fixtures/smoke/graphql-with-mjs/src/index.js
new file mode 100644
index 00000000000..b597a44232c
--- /dev/null
+++ b/fixtures/smoke/graphql-with-mjs/src/index.js
@@ -0,0 +1,5 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+
+ReactDOM.render( , document.getElementById('root'));
diff --git a/fixtures/smoke/issue-5176-flow-class-properties/index.test.js b/fixtures/smoke/issue-5176-flow-class-properties/index.test.js
new file mode 100644
index 00000000000..72a7a3daf0e
--- /dev/null
+++ b/fixtures/smoke/issue-5176-flow-class-properties/index.test.js
@@ -0,0 +1,13 @@
+const { bootstrap, isSuccessfulTest } = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('issue #5176 (flow class properties interaction)', () => {
+ it('passes tests', async () => {
+ await isSuccessfulTest({
+ directory: global.testDirectory,
+ jestEnvironment: 'node',
+ });
+ });
+});
diff --git a/fixtures/smoke/issue-5176-flow-class-properties/package.json b/fixtures/smoke/issue-5176-flow-class-properties/package.json
new file mode 100644
index 00000000000..0967ef424bc
--- /dev/null
+++ b/fixtures/smoke/issue-5176-flow-class-properties/package.json
@@ -0,0 +1 @@
+{}
diff --git a/fixtures/smoke/issue-5176-flow-class-properties/public/index.html b/fixtures/smoke/issue-5176-flow-class-properties/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/issue-5176-flow-class-properties/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/issue-5176-flow-class-properties/src/App.js b/fixtures/smoke/issue-5176-flow-class-properties/src/App.js
new file mode 100644
index 00000000000..c6a68613b58
--- /dev/null
+++ b/fixtures/smoke/issue-5176-flow-class-properties/src/App.js
@@ -0,0 +1,11 @@
+class App {
+ constructor() {
+ this.foo = this.foo.bind(this);
+ }
+ foo: void => void;
+ foo() {
+ return 'bar';
+ }
+}
+
+export default App;
diff --git a/fixtures/smoke/issue-5176-flow-class-properties/src/App.test.js b/fixtures/smoke/issue-5176-flow-class-properties/src/App.test.js
new file mode 100644
index 00000000000..4991b756f29
--- /dev/null
+++ b/fixtures/smoke/issue-5176-flow-class-properties/src/App.test.js
@@ -0,0 +1,6 @@
+import App from './App';
+
+it('creates instance without', () => {
+ const app = new App();
+ expect(app.foo()).toBe('bar');
+});
diff --git a/fixtures/smoke/jest.config.js b/fixtures/smoke/jest.config.js
new file mode 100644
index 00000000000..b2f8182ebd9
--- /dev/null
+++ b/fixtures/smoke/jest.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ testEnvironment: 'node',
+ testMatch: ['**/*.test.js'],
+ testPathIgnorePatterns: ['/src/', 'node_modules'],
+ setupTestFrameworkScriptFile: './setupSmokeTests.js',
+};
diff --git a/fixtures/smoke/relative-paths/index.test.js b/fixtures/smoke/relative-paths/index.test.js
new file mode 100644
index 00000000000..1f367f64350
--- /dev/null
+++ b/fixtures/smoke/relative-paths/index.test.js
@@ -0,0 +1,37 @@
+const fs = require('fs-extra');
+const globby = require('globby');
+const path = require('path');
+const {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+} = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('relative paths', () => {
+ // TODO: enable when development relative paths are supported
+ xit('builds in development', async () => {
+ await isSuccessfulDevelopment({ directory: global.testDirectory });
+ });
+ it('builds in production', async () => {
+ await isSuccessfulProduction({ directory: global.testDirectory });
+
+ const buildDir = path.join(global.testDirectory, 'build');
+ const cssFile = path.join(
+ buildDir,
+ globby.sync('**/*.css', { cwd: buildDir }).pop()
+ );
+ const svgFile = path.join(
+ buildDir,
+ globby.sync('**/*.svg', { cwd: buildDir }).pop()
+ );
+ const desiredPath = /url\((.+?)\)/
+ .exec(fs.readFileSync(cssFile, 'utf8'))
+ .pop();
+ expect(path.resolve(path.join(path.dirname(cssFile), desiredPath))).toBe(
+ path.resolve(svgFile)
+ );
+ });
+});
diff --git a/fixtures/smoke/relative-paths/package.json b/fixtures/smoke/relative-paths/package.json
new file mode 100644
index 00000000000..64d2bb6dd27
--- /dev/null
+++ b/fixtures/smoke/relative-paths/package.json
@@ -0,0 +1,4 @@
+{
+ "dependencies": {},
+ "homepage": "."
+}
diff --git a/fixtures/smoke/relative-paths/public/index.html b/fixtures/smoke/relative-paths/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/relative-paths/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/relative-paths/src/index.css b/fixtures/smoke/relative-paths/src/index.css
new file mode 100644
index 00000000000..244889b10aa
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/index.css
@@ -0,0 +1,8 @@
+.RootSvg:before {
+ display: block;
+ content: ' ';
+ background-image: url(./logo.svg);
+ background-size: 28px 28px;
+ height: 28px;
+ width: 28px;
+}
diff --git a/fixtures/smoke/relative-paths/src/index.js b/fixtures/smoke/relative-paths/src/index.js
new file mode 100644
index 00000000000..6a9a4b13285
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/index.js
@@ -0,0 +1 @@
+import './index.css';
diff --git a/fixtures/smoke/relative-paths/src/logo.svg b/fixtures/smoke/relative-paths/src/logo.svg
new file mode 100644
index 00000000000..5e53156653f
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/logo.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/fixtures/smoke/setupSmokeTests.js b/fixtures/smoke/setupSmokeTests.js
new file mode 100644
index 00000000000..1d4038de417
--- /dev/null
+++ b/fixtures/smoke/setupSmokeTests.js
@@ -0,0 +1,10 @@
+const fs = require('fs-extra');
+const tempy = require('tempy');
+
+beforeEach(() => {
+ global.testDirectory = tempy.directory();
+ jest.setTimeout(1000 * 60 * 5);
+});
+afterEach(() => {
+ fs.removeSync(global.testDirectory);
+});
diff --git a/fixtures/utils.js b/fixtures/utils.js
new file mode 100644
index 00000000000..bde092f6e3e
--- /dev/null
+++ b/fixtures/utils.js
@@ -0,0 +1,145 @@
+const execa = require('execa');
+const fs = require('fs-extra');
+const getPort = require('get-port');
+const path = require('path');
+const os = require('os');
+const stripAnsi = require('strip-ansi');
+
+async function bootstrap({ directory, template }) {
+ const shouldInstallScripts = process.env.CI && process.env.CI !== 'false';
+ await Promise.all(
+ ['public/', 'src/', 'package.json'].map(async file =>
+ fs.copy(path.join(template, file), path.join(directory, file))
+ )
+ );
+ if (shouldInstallScripts) {
+ const packageJson = fs.readJsonSync(path.join(directory, 'package.json'));
+ packageJson.dependencies = Object.assign({}, packageJson.dependencies, {
+ 'react-scripts': 'latest',
+ });
+ fs.writeJsonSync(path.join(directory, 'package.json'), packageJson);
+ }
+ await execa('yarnpkg', ['install', '--mutex', 'network'], { cwd: directory });
+ if (!shouldInstallScripts) {
+ fs.ensureSymlinkSync(
+ path.resolve(
+ path.join(
+ __dirname,
+ '..',
+ 'packages',
+ 'react-scripts',
+ 'bin',
+ 'react-scripts.js'
+ )
+ ),
+ path.join(directory, 'node_modules', '.bin', 'react-scripts')
+ );
+ await execa('yarnpkg', ['link', 'react-scripts'], { cwd: directory });
+ }
+}
+
+async function isSuccessfulDevelopment({ directory }) {
+ const { stdout, stderr } = await execa(
+ './node_modules/.bin/react-scripts',
+ ['start', '--smoke-test'],
+ {
+ cwd: directory,
+ env: { BROWSER: 'none', PORT: await getPort() },
+ }
+ );
+
+ if (!/Compiled successfully/.test(stdout)) {
+ throw new Error(`stdout: ${stdout}${os.EOL + os.EOL}stderr: ${stderr}`);
+ }
+}
+
+async function isSuccessfulProduction({ directory }) {
+ const { stdout, stderr } = await execa(
+ './node_modules/.bin/react-scripts',
+ ['build'],
+ {
+ cwd: directory,
+ }
+ );
+
+ if (!/Compiled successfully/.test(stdout)) {
+ throw new Error(`stdout: ${stdout}${os.EOL + os.EOL}stderr: ${stderr}`);
+ }
+}
+
+async function isSuccessfulTest({ directory, jestEnvironment = 'jsdom' }) {
+ await execa(
+ './node_modules/.bin/react-scripts',
+ ['test', '--env', jestEnvironment, '--ci'],
+ {
+ cwd: directory,
+ env: { CI: 'true' },
+ }
+ );
+}
+
+async function getOutputDevelopment({ directory, env = {} }) {
+ try {
+ const { stdout, stderr } = await execa(
+ './node_modules/.bin/react-scripts',
+ ['start', '--smoke-test'],
+ {
+ cwd: directory,
+ env: Object.assign(
+ {},
+ {
+ BROWSER: 'none',
+ PORT: await getPort(),
+ CI: 'false',
+ FORCE_COLOR: '0',
+ },
+ env
+ ),
+ }
+ );
+ return { stdout: stripAnsi(stdout), stderr: stripAnsi(stderr) };
+ } catch (err) {
+ return {
+ stdout: '',
+ stderr: stripAnsi(
+ err.message
+ .split(os.EOL)
+ .slice(2)
+ .join(os.EOL)
+ ),
+ };
+ }
+}
+
+async function getOutputProduction({ directory, env = {} }) {
+ try {
+ const { stdout, stderr } = await execa(
+ './node_modules/.bin/react-scripts',
+ ['build'],
+ {
+ cwd: directory,
+ env: Object.assign({}, { CI: 'false', FORCE_COLOR: '0' }, env),
+ }
+ );
+ return { stdout: stripAnsi(stdout), stderr: stripAnsi(stderr) };
+ } catch (err) {
+ return {
+ stdout: '',
+ stderr: stripAnsi(
+ err.message
+ .split(os.EOL)
+ .slice(2)
+ .join(os.EOL)
+ ),
+ };
+ }
+}
+
+module.exports = {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+ isSuccessfulTest,
+ getOutputDevelopment,
+ getOutputProduction,
+};
diff --git a/package.json b/package.json
index 9a590471c3c..96d63b4f224 100644
--- a/package.json
+++ b/package.json
@@ -14,25 +14,35 @@
"start": "cd packages/react-scripts && node bin/react-scripts.js start",
"screencast": "node ./tasks/screencast.js",
"screencast:error": "svg-term --cast jyu19xGl88FQ3poMY8Hbmfw8y --out screencast-error.svg --window --at 12000 --no-cursor",
- "test": "cd packages/react-scripts && node bin/react-scripts.js test --env=jsdom",
- "format": "prettier --trailing-comma es5 --single-quote --write 'packages/*/*.js' 'packages/*/!(node_modules)/**/*.js'",
- "precommit": "lint-staged"
+ "test": "cd packages/react-scripts && node bin/react-scripts.js test",
+ "format": "prettier --trailing-comma es5 --single-quote --write 'packages/*/*.js' 'packages/*/!(node_modules)/**/*.js'"
},
"devDependencies": {
- "eslint": "4.15.0",
- "execa": "^0.9.0",
- "husky": "^0.13.2",
- "lerna": "2.6.0",
- "lerna-changelog": "^0.6.0",
- "lint-staged": "^3.3.1",
- "meow": "^4.0.0",
+ "async-sema": "^2.1.3",
+ "eslint": "5.6.0",
+ "execa": "1.0.0",
+ "fs-extra": "^7.0.0",
+ "get-port": "^4.0.0",
+ "globby": "^8.0.1",
+ "husky": "1.0.0-rc.15",
+ "jest": "^23.6.0",
+ "lerna": "2.9.1",
+ "lerna-changelog": "^0.8.0",
+ "lint-staged": "^7.0.5",
+ "meow": "^5.0.0",
"multimatch": "^2.1.0",
- "prettier": "1.6.1",
- "svg-term-cli": "^2.0.3",
+ "prettier": "1.14.3",
+ "strip-ansi": "^4.0.0",
+ "svg-term-cli": "^2.1.1",
"tempy": "^0.2.1"
},
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
"lint-staged": {
- "*.js": [
+ "*.{js,md,css}": [
"prettier --trailing-comma es5 --single-quote --write",
"git add"
],
diff --git a/packages/babel-plugin-named-asset-import/LICENSE b/packages/babel-plugin-named-asset-import/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/babel-plugin-named-asset-import/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/babel-plugin-named-asset-import/index.js b/packages/babel-plugin-named-asset-import/index.js
index 6fd919bc676..f1989e5ee06 100644
--- a/packages/babel-plugin-named-asset-import/index.js
+++ b/packages/babel-plugin-named-asset-import/index.js
@@ -7,7 +7,12 @@ function namedAssetImportPlugin({ types: t }) {
return {
visitor: {
- ImportDeclaration(path, { opts: { loaderMap } }) {
+ ImportDeclaration(
+ path,
+ {
+ opts: { loaderMap },
+ }
+ ) {
const sourcePath = path.node.source.value;
const ext = extname(sourcePath).substr(1);
diff --git a/packages/babel-plugin-named-asset-import/package.json b/packages/babel-plugin-named-asset-import/package.json
index 27bfae4b0e2..192e141f750 100644
--- a/packages/babel-plugin-named-asset-import/package.json
+++ b/packages/babel-plugin-named-asset-import/package.json
@@ -1,6 +1,6 @@
{
"name": "babel-plugin-named-asset-import",
- "version": "0.1.0",
+ "version": "0.2.1",
"description": "Babel plugin for named asset imports in Create React App",
"repository": "facebookincubator/create-react-app",
"license": "MIT",
@@ -12,6 +12,6 @@
"index.js"
],
"peerDependencies": {
- "@babel/core": "7.0.0-beta.44"
+ "@babel/core": "^7.1.0"
}
}
diff --git a/packages/babel-preset-react-app/LICENSE b/packages/babel-preset-react-app/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/babel-preset-react-app/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js
new file mode 100644
index 00000000000..52f0fa14bb7
--- /dev/null
+++ b/packages/babel-preset-react-app/create.js
@@ -0,0 +1,162 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const path = require('path');
+
+const validateBoolOption = (name, value, defaultValue) => {
+ if (typeof value === 'undefined') {
+ value = defaultValue;
+ }
+
+ if (typeof value !== 'boolean') {
+ throw new Error(`Preset react-app: '${name}' option must be a boolean.`);
+ }
+
+ return value;
+};
+
+module.exports = function(api, opts, env) {
+ if (!opts) {
+ opts = {};
+ }
+
+ var isEnvDevelopment = env === 'development';
+ var isEnvProduction = env === 'production';
+ var isEnvTest = env === 'test';
+
+ var isFlowEnabled = validateBoolOption('flow', opts.flow, true);
+ var areHelpersEnabled = validateBoolOption('helpers', opts.helpers, true);
+ var useAbsoluteRuntime = validateBoolOption(
+ 'absoluteRuntime',
+ opts.absoluteRuntime,
+ true
+ );
+
+ var absoluteRuntimePath = undefined;
+ if (useAbsoluteRuntime) {
+ absoluteRuntimePath = path.dirname(
+ require.resolve('@babel/runtime/package.json')
+ );
+ }
+
+ if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) {
+ throw new Error(
+ 'Using `babel-preset-react-app` requires that you specify `NODE_ENV` or ' +
+ '`BABEL_ENV` environment variables. Valid values are "development", ' +
+ '"test", and "production". Instead, received: ' +
+ JSON.stringify(env) +
+ '.'
+ );
+ }
+
+ return {
+ presets: [
+ isEnvTest && [
+ // ES features necessary for user's Node version
+ require('@babel/preset-env').default,
+ {
+ targets: {
+ node: 'current',
+ },
+ },
+ ],
+ (isEnvProduction || isEnvDevelopment) && [
+ // Latest stable ECMAScript features
+ require('@babel/preset-env').default,
+ {
+ // We want Create React App to be IE 9 compatible until React itself
+ // no longer works with IE 9
+ targets: {
+ ie: 9,
+ },
+ // Users cannot override this behavior because this Babel
+ // configuration is highly tuned for ES5 support
+ ignoreBrowserslistConfig: true,
+ // If users import all core-js they're probably not concerned with
+ // bundle size. We shouldn't rely on magic to try and shrink it.
+ useBuiltIns: false,
+ // Do not transform modules to CJS
+ modules: false,
+ },
+ ],
+ [
+ require('@babel/preset-react').default,
+ {
+ // Adds component stack to warning messages
+ // Adds __self attribute to JSX which React will use for some warnings
+ development: isEnvDevelopment || isEnvTest,
+ // Will use the native built-in instead of trying to polyfill
+ // behavior for any plugins that require one.
+ useBuiltIns: true,
+ },
+ ],
+ ].filter(Boolean),
+ plugins: [
+ // Strip flow types before any other transform, emulating the behavior
+ // order as-if the browser supported all of the succeeding features
+ // https://github.com/facebook/create-react-app/pull/5182
+ isFlowEnabled &&
+ require('@babel/plugin-transform-flow-strip-types').default,
+ // Experimental macros support. Will be documented after it's had some time
+ // in the wild.
+ require('babel-plugin-macros'),
+ // Necessary to include regardless of the environment because
+ // in practice some other transforms (such as object-rest-spread)
+ // don't work without it: https://github.com/babel/babel/issues/7215
+ require('@babel/plugin-transform-destructuring').default,
+ // class { handleClick = () => { } }
+ // Enable loose mode to use assignment instead of defineProperty
+ // See discussion in https://github.com/facebook/create-react-app/issues/4263
+ [
+ require('@babel/plugin-proposal-class-properties').default,
+ {
+ loose: true,
+ },
+ ],
+ // 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('@babel/plugin-proposal-object-rest-spread').default,
+ {
+ useBuiltIns: true,
+ },
+ ],
+ // Polyfills the runtime needed for async/await, generators, and friends
+ // https://babeljs.io/docs/en/babel-plugin-transform-runtime
+ [
+ require('@babel/plugin-transform-runtime').default,
+ {
+ corejs: false,
+ helpers: areHelpersEnabled,
+ regenerator: true,
+ // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
+ // We should turn this on once the lowest version of Node LTS
+ // supports ES Modules.
+ useESModules: isEnvDevelopment || isEnvProduction,
+ // Undocumented option that lets us encapsulate our runtime, ensuring
+ // the correct version is used
+ // https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42
+ absoluteRuntime: absoluteRuntimePath,
+ },
+ ],
+ isEnvProduction && [
+ // Remove PropTypes from production build
+ require('babel-plugin-transform-react-remove-prop-types').default,
+ {
+ removeImport: true,
+ },
+ ],
+ // Adds syntax support for import()
+ require('@babel/plugin-syntax-dynamic-import').default,
+ isEnvTest &&
+ // Transform dynamic import to require
+ require('babel-plugin-transform-dynamic-import').default,
+ ].filter(Boolean),
+ };
+};
diff --git a/packages/babel-preset-react-app/dependencies.js b/packages/babel-preset-react-app/dependencies.js
index b676945a311..19a6e74557b 100644
--- a/packages/babel-preset-react-app/dependencies.js
+++ b/packages/babel-preset-react-app/dependencies.js
@@ -6,6 +6,20 @@
*/
'use strict';
+const path = require('path');
+
+const validateBoolOption = (name, value, defaultValue) => {
+ if (typeof value === 'undefined') {
+ value = defaultValue;
+ }
+
+ if (typeof value !== 'boolean') {
+ throw new Error(`Preset react-app: '${name}' option must be a boolean.`);
+ }
+
+ return value;
+};
+
module.exports = function(api, opts) {
if (!opts) {
opts = {};
@@ -21,6 +35,21 @@ module.exports = function(api, opts) {
var isEnvDevelopment = env === 'development';
var isEnvProduction = env === 'production';
var isEnvTest = env === 'test';
+
+ var areHelpersEnabled = validateBoolOption('helpers', opts.helpers, false);
+ var useAbsoluteRuntime = validateBoolOption(
+ 'absoluteRuntime',
+ opts.absoluteRuntime,
+ true
+ );
+
+ var absoluteRuntimePath = undefined;
+ if (useAbsoluteRuntime) {
+ absoluteRuntimePath = path.dirname(
+ require.resolve('@babel/runtime/package.json')
+ );
+ }
+
if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) {
throw new Error(
'Using `babel-preset-react-app` requires that you specify `NODE_ENV` or ' +
@@ -32,6 +61,11 @@ module.exports = function(api, opts) {
}
return {
+ // Babel assumes ES Modules, which isn't safe until CommonJS
+ // dies. This changes the behavior to assume CommonJS unless
+ // an `import` or `export` is present in the file.
+ // https://github.com/webpack/webpack/issues/4039#issuecomment-419284940
+ sourceType: 'unambiguous',
presets: [
isEnvTest && [
// ES features necessary for user's Node version
@@ -48,10 +82,46 @@ module.exports = function(api, opts) {
// Latest stable ECMAScript features
require('@babel/preset-env').default,
{
+ // We want Create React App to be IE 9 compatible until React itself
+ // no longer works with IE 9
+ targets: {
+ ie: 9,
+ },
+ // Users cannot override this behavior because this Babel
+ // configuration is highly tuned for ES5 support
+ ignoreBrowserslistConfig: true,
+ // If users import all core-js they're probably not concerned with
+ // bundle size. We shouldn't rely on magic to try and shrink it.
+ useBuiltIns: false,
// Do not transform modules to CJS
modules: false,
},
],
].filter(Boolean),
+ plugins: [
+ // Polyfills the runtime needed for async/await, generators, and friends
+ // https://babeljs.io/docs/en/babel-plugin-transform-runtime
+ [
+ require('@babel/plugin-transform-runtime').default,
+ {
+ corejs: false,
+ helpers: areHelpersEnabled,
+ regenerator: true,
+ // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules
+ // We should turn this on once the lowest version of Node LTS
+ // supports ES Modules.
+ useESModules: isEnvDevelopment || isEnvProduction,
+ // Undocumented option that lets us encapsulate our runtime, ensuring
+ // the correct version is used
+ // https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42
+ absoluteRuntime: absoluteRuntimePath,
+ },
+ ],
+ // Adds syntax support for import()
+ require('@babel/plugin-syntax-dynamic-import').default,
+ isEnvTest &&
+ // Transform dynamic import to require
+ require('babel-plugin-transform-dynamic-import').default,
+ ].filter(Boolean),
};
};
diff --git a/packages/babel-preset-react-app/dev.js b/packages/babel-preset-react-app/dev.js
new file mode 100644
index 00000000000..ce87adea4fa
--- /dev/null
+++ b/packages/babel-preset-react-app/dev.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const create = require('./create');
+
+module.exports = function(api, opts) {
+ return create(api, Object.assign({ helpers: false }, opts), 'development');
+};
diff --git a/packages/babel-preset-react-app/index.js b/packages/babel-preset-react-app/index.js
index 72089b9b78b..621af951619 100644
--- a/packages/babel-preset-react-app/index.js
+++ b/packages/babel-preset-react-app/index.js
@@ -6,138 +6,15 @@
*/
'use strict';
-const validateBoolOption = (name, value, defaultValue) => {
- if (typeof value === 'undefined') {
- value = defaultValue;
- }
-
- if (typeof value !== 'boolean') {
- throw new Error(`Preset react-app: '${name}' option must be a boolean.`);
- }
-
- return value;
-};
+const create = require('./create');
module.exports = function(api, opts) {
- if (!opts) {
- opts = {};
- }
-
// This is similar to how `env` works in Babel:
// https://babeljs.io/docs/usage/babelrc/#env-option
// We are not using `env` because it’s ignored in versions > babel-core@6.10.4:
// https://github.com/babel/babel/issues/4539
// https://github.com/facebook/create-react-app/issues/720
// It’s also nice that we can enforce `NODE_ENV` being specified.
- var env = process.env.BABEL_ENV || process.env.NODE_ENV;
- var isEnvDevelopment = env === 'development';
- var isEnvProduction = env === 'production';
- var isEnvTest = env === 'test';
- var isFlowEnabled = validateBoolOption('flow', opts.flow, true);
-
- if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) {
- throw new Error(
- 'Using `babel-preset-react-app` requires that you specify `NODE_ENV` or ' +
- '`BABEL_ENV` environment variables. Valid values are "development", ' +
- '"test", and "production". Instead, received: ' +
- JSON.stringify(env) +
- '.'
- );
- }
-
- return {
- presets: [
- isEnvTest && [
- // ES features necessary for user's Node version
- require('@babel/preset-env').default,
- {
- targets: {
- node: '6.12',
- },
- },
- ],
- (isEnvProduction || isEnvDevelopment) && [
- // Latest stable ECMAScript features
- require('@babel/preset-env').default,
- {
- // `entry` transforms `@babel/polyfill` into individual requires for
- // the targeted browsers. This is safer than `usage` which performs
- // static code analysis to determine what's required.
- // This is probably a fine default to help trim down bundles when
- // end-users inevitably import '@babel/polyfill'.
- useBuiltIns: 'entry',
- // Do not transform modules to CJS
- modules: false,
- },
- ],
- [
- require('@babel/preset-react').default,
- {
- // Adds component stack to warning messages
- // Adds __self attribute to JSX which React will use for some warnings
- development: isEnvDevelopment || isEnvTest,
- // Will use the native built-in instead of trying to polyfill
- // behavior for any plugins that require one.
- useBuiltIns: true,
- },
- ],
- isFlowEnabled && [require('@babel/preset-flow').default],
- ].filter(Boolean),
- plugins: [
- // Experimental macros support. Will be documented after it's had some time
- // in the wild.
- require('babel-plugin-macros'),
- // Necessary to include regardless of the environment because
- // in practice some other transforms (such as object-rest-spread)
- // don't work without it: https://github.com/babel/babel/issues/7215
- require('@babel/plugin-transform-destructuring').default,
- // class { handleClick = () => { } }
- // Enable loose mode to use assignment instead of defineProperty
- // See discussion in https://github.com/facebook/create-react-app/issues/4263
- [
- require('@babel/plugin-proposal-class-properties').default,
- {
- loose: true,
- },
- ],
- // 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('@babel/plugin-proposal-object-rest-spread').default,
- {
- useBuiltIns: true,
- },
- ],
- // Polyfills the runtime needed for async/await and generators
- [
- require('@babel/plugin-transform-runtime').default,
- {
- helpers: false,
- polyfill: false,
- regenerator: true,
- },
- ],
- isEnvProduction && [
- // Remove PropTypes from production build
- require('babel-plugin-transform-react-remove-prop-types').default,
- {
- removeImport: true,
- },
- ],
- // function* () { yield 42; yield 43; }
- !isEnvTest && [
- require('@babel/plugin-transform-regenerator').default,
- {
- // Async functions are converted to generators by @babel/preset-env
- async: false,
- },
- ],
- // Adds syntax support for import()
- require('@babel/plugin-syntax-dynamic-import').default,
- isEnvTest &&
- // Transform dynamic import to require
- require('babel-plugin-transform-dynamic-import').default,
- ].filter(Boolean),
- };
+ const env = process.env.BABEL_ENV || process.env.NODE_ENV;
+ return create(api, opts, env);
};
diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json
index be38c048947..10593914103 100644
--- a/packages/babel-preset-react-app/package.json
+++ b/packages/babel-preset-react-app/package.json
@@ -1,6 +1,6 @@
{
"name": "babel-preset-react-app",
- "version": "3.1.1",
+ "version": "5.0.1",
"description": "Babel preset used by Create React App",
"repository": "facebook/create-react-app",
"license": "MIT",
@@ -8,25 +8,31 @@
"url": "https://github.com/facebook/create-react-app/issues"
},
"files": [
+ "create.js",
+ "dependencies.js",
+ "dev.js",
"index.js",
- "dependencies.js"
+ "webpack-overrides.js",
+ "prod.js",
+ "test.js"
],
"dependencies": {
- "@babel/core": "7.0.0-beta.44",
- "@babel/plugin-proposal-class-properties": "7.0.0-beta.44",
- "@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.44",
- "@babel/plugin-syntax-dynamic-import": "7.0.0-beta.44",
- "@babel/plugin-transform-classes": "7.0.0-beta.44",
- "@babel/plugin-transform-destructuring": "7.0.0-beta.44",
- "@babel/plugin-transform-react-constant-elements": "7.0.0-beta.44",
- "@babel/plugin-transform-react-display-name": "7.0.0-beta.44",
- "@babel/plugin-transform-regenerator": "7.0.0-beta.44",
- "@babel/plugin-transform-runtime": "7.0.0-beta.44",
- "@babel/preset-env": "7.0.0-beta.44",
- "@babel/preset-flow": "7.0.0-beta.44",
- "@babel/preset-react": "7.0.0-beta.44",
- "babel-plugin-macros": "2.0.0",
- "babel-plugin-transform-dynamic-import": "2.0.0",
- "babel-plugin-transform-react-remove-prop-types": "0.4.12"
+ "@babel/core": "7.1.0",
+ "@babel/plugin-proposal-class-properties": "7.1.0",
+ "@babel/plugin-proposal-object-rest-spread": "7.0.0",
+ "@babel/plugin-syntax-dynamic-import": "7.0.0",
+ "@babel/plugin-transform-classes": "7.1.0",
+ "@babel/plugin-transform-destructuring": "7.0.0",
+ "@babel/plugin-transform-flow-strip-types": "7.0.0",
+ "@babel/plugin-transform-react-constant-elements": "7.0.0",
+ "@babel/plugin-transform-react-display-name": "7.0.0",
+ "@babel/plugin-transform-runtime": "7.1.0",
+ "@babel/preset-env": "7.1.0",
+ "@babel/preset-react": "7.0.0",
+ "@babel/runtime": "7.0.0",
+ "babel-loader": "8.0.4",
+ "babel-plugin-macros": "2.4.2",
+ "babel-plugin-transform-dynamic-import": "2.1.0",
+ "babel-plugin-transform-react-remove-prop-types": "0.4.18"
}
}
diff --git a/packages/babel-preset-react-app/prod.js b/packages/babel-preset-react-app/prod.js
new file mode 100644
index 00000000000..0873aee7258
--- /dev/null
+++ b/packages/babel-preset-react-app/prod.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const create = require('./create');
+
+module.exports = function(api, opts) {
+ return create(api, Object.assign({ helpers: false }, opts), 'production');
+};
diff --git a/packages/babel-preset-react-app/test.js b/packages/babel-preset-react-app/test.js
new file mode 100644
index 00000000000..be189fc3265
--- /dev/null
+++ b/packages/babel-preset-react-app/test.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const create = require('./create');
+
+module.exports = function(api, opts) {
+ return create(api, Object.assign({ helpers: false }, opts), 'test');
+};
diff --git a/packages/babel-preset-react-app/webpack-overrides.js b/packages/babel-preset-react-app/webpack-overrides.js
new file mode 100644
index 00000000000..95a87eeb66f
--- /dev/null
+++ b/packages/babel-preset-react-app/webpack-overrides.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const crypto = require('crypto');
+
+const macroCheck = new RegExp('[./]macro');
+
+module.exports = function() {
+ return {
+ // This function transforms the Babel configuration on a per-file basis
+ config(config, { source }) {
+ // Babel Macros are notoriously hard to cache, so they shouldn't be
+ // https://github.com/babel/babel/issues/8497
+ // We naively detect macros using their package suffix and add a random token
+ // to the caller, a valid option accepted by Babel, to compose a one-time
+ // cacheIdentifier for the file. We cannot tune the loader options on a per
+ // file basis.
+ if (macroCheck.test(source)) {
+ return Object.assign({}, config.options, {
+ caller: Object.assign({}, config.options.caller, {
+ craInvalidationToken: crypto.randomBytes(32).toString('hex'),
+ }),
+ });
+ }
+ return config.options;
+ },
+ };
+};
diff --git a/packages/confusing-browser-globals/LICENSE b/packages/confusing-browser-globals/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/confusing-browser-globals/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/confusing-browser-globals/package.json b/packages/confusing-browser-globals/package.json
index b96cff9ef93..f2d9d87e2ad 100644
--- a/packages/confusing-browser-globals/package.json
+++ b/packages/confusing-browser-globals/package.json
@@ -1,6 +1,6 @@
{
"name": "confusing-browser-globals",
- "version": "1.0.0",
+ "version": "1.0.2",
"description": "A list of browser globals that are often used by mistake instead of local variables",
"license": "MIT",
"main": "index.js",
@@ -16,6 +16,6 @@
"index.js"
],
"devDependencies": {
- "jest": "22.1.2"
+ "jest": "23.6.0"
}
}
diff --git a/packages/create-react-app/LICENSE b/packages/create-react-app/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/create-react-app/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js
index cbe9699e2c3..132d55a8373 100755
--- a/packages/create-react-app/createReactApp.js
+++ b/packages/create-react-app/createReactApp.js
@@ -49,7 +49,7 @@ const url = require('url');
const hyperquest = require('hyperquest');
const envinfo = require('envinfo');
const os = require('os');
-const findMonorepo = require('react-dev-utils/workspaceUtils').findMonorepo;
+
const packageJson = require('./package.json');
// These files should be allowed to remain on a failed install,
@@ -76,6 +76,7 @@ const program = new commander.Command(packageJson.name)
'use a non-standard version of react-scripts'
)
.option('--use-npm')
+ .option('--use-pnp')
.allowUnknownOption()
.on('--help', () => {
console.log(` Only ${chalk.green('')} is required.`);
@@ -84,6 +85,7 @@ const program = new commander.Command(packageJson.name)
` A custom ${chalk.cyan('--scripts-version')} can be one of:`
);
console.log(` - a specific npm version: ${chalk.green('0.8.2')}`);
+ console.log(` - a specific npm tag: ${chalk.green('@next')}`);
console.log(
` - a custom fork published on npm: ${chalk.green(
'my-react-scripts'
@@ -120,15 +122,28 @@ const program = new commander.Command(packageJson.name)
})
.parse(process.argv);
+if (program.info) {
+ console.log(chalk.bold('\nEnvironment Info:'));
+ return envinfo
+ .run(
+ {
+ System: ['OS', 'CPU'],
+ Binaries: ['Node', 'npm', 'Yarn'],
+ Browsers: ['Chrome', 'Edge', 'Internet Explorer', 'Firefox', 'Safari'],
+ npmPackages: ['react', 'react-dom', 'react-scripts'],
+ npmGlobalPackages: ['create-react-app'],
+ },
+ {
+ clipboard: true,
+ duplicates: true,
+ showNotFound: true,
+ }
+ )
+ .then(console.log)
+ .then(() => console.log(chalk.green('Copied To Clipboard!\n')));
+}
+
if (typeof projectName === 'undefined') {
- if (program.info) {
- envinfo.print({
- packages: ['react', 'react-dom', 'react-scripts'],
- noNativeIDE: true,
- duplicates: true,
- });
- process.exit(0);
- }
console.error('Please specify the project directory:');
console.log(
` ${chalk.cyan(program.name())} ${chalk.green('')}`
@@ -164,10 +179,11 @@ createApp(
program.verbose,
program.scriptsVersion,
program.useNpm,
+ program.usePnp,
hiddenProgram.internalTestingTemplate
);
-function createApp(name, verbose, version, useNpm, template) {
+function createApp(name, verbose, version, useNpm, usePnp, template) {
const root = path.resolve(name);
const appName = path.basename(root);
@@ -190,7 +206,7 @@ function createApp(name, verbose, version, useNpm, template) {
JSON.stringify(packageJson, null, 2) + os.EOL
);
- const useYarn = useNpm ? false : shouldUseYarn(root);
+ const useYarn = useNpm ? false : shouldUseYarn();
const originalDirectory = process.cwd();
process.chdir(root);
if (!useYarn && !checkThatNpmCanReadCwd()) {
@@ -200,7 +216,9 @@ function createApp(name, verbose, version, useNpm, template) {
if (!semver.satisfies(process.version, '>=6.0.0')) {
console.log(
chalk.yellow(
- `You are using Node ${process.version} so the project will be bootstrapped with an old unsupported version of tools.\n\n` +
+ `You are using Node ${
+ process.version
+ } so the project will be bootstrapped with an old unsupported version of tools.\n\n` +
`Please update to Node 6 or higher for a better, fully supported experience.\n`
)
);
@@ -214,7 +232,9 @@ function createApp(name, verbose, version, useNpm, template) {
if (npmInfo.npmVersion) {
console.log(
chalk.yellow(
- `You are using npm ${npmInfo.npmVersion} so the project will be boostrapped with an old unsupported version of tools.\n\n` +
+ `You are using npm ${
+ npmInfo.npmVersion
+ } so the project will be boostrapped with an old unsupported version of tools.\n\n` +
`Please update to npm 3 or higher for a better, fully supported experience.\n`
)
);
@@ -223,10 +243,19 @@ function createApp(name, verbose, version, useNpm, template) {
version = 'react-scripts@0.9.x';
}
}
- run(root, appName, version, verbose, originalDirectory, template, useYarn);
+ run(
+ root,
+ appName,
+ version,
+ verbose,
+ originalDirectory,
+ template,
+ useYarn,
+ usePnp
+ );
}
-function isYarnAvailable() {
+function shouldUseYarn() {
try {
execSync('yarnpkg --version', { stdio: 'ignore' });
return true;
@@ -235,12 +264,7 @@ function isYarnAvailable() {
}
}
-function shouldUseYarn(appDir) {
- const mono = findMonorepo(appDir);
- return (mono.isYarnWs && mono.isAppIncluded) || isYarnAvailable();
-}
-
-function install(root, useYarn, dependencies, verbose, isOnline) {
+function install(root, useYarn, usePnp, dependencies, verbose, isOnline) {
return new Promise((resolve, reject) => {
let command;
let args;
@@ -250,6 +274,9 @@ function install(root, useYarn, dependencies, verbose, isOnline) {
if (!isOnline) {
args.push('--offline');
}
+ if (usePnp) {
+ args.push('--enable-pnp');
+ }
[].push.apply(args, dependencies);
// Explicitly set cwd() to work around issues like
@@ -274,6 +301,12 @@ function install(root, useYarn, dependencies, verbose, isOnline) {
'--loglevel',
'error',
].concat(dependencies);
+
+ if (usePnp) {
+ console.log(chalk.yellow("NPM doesn't support PnP."));
+ console.log(chalk.yellow('Falling back to the regular installs.'));
+ console.log();
+ }
}
if (verbose) {
@@ -300,7 +333,8 @@ function run(
verbose,
originalDirectory,
template,
- useYarn
+ useYarn,
+ usePnp
) {
const packageToInstall = getInstallPackage(version, originalDirectory);
const allDependencies = ['react', 'react-dom', packageToInstall];
@@ -323,28 +357,39 @@ function run(
);
console.log();
- return install(root, useYarn, allDependencies, verbose, isOnline).then(
- () => packageName
- );
+ return install(
+ root,
+ useYarn,
+ usePnp,
+ allDependencies,
+ verbose,
+ isOnline
+ ).then(() => packageName);
})
- .then(packageName => {
+ .then(async packageName => {
checkNodeVersion(packageName);
setCaretRangeForRuntimeDeps(packageName);
- const scriptsPath = path.resolve(
- process.cwd(),
- 'node_modules',
- packageName,
- 'scripts',
- 'init.js'
+ const pnpPath = path.resolve(process.cwd(), '.pnp.js');
+
+ const nodeArgs = fs.existsSync(pnpPath) ? ['--require', pnpPath] : [];
+
+ await executeNodeScript(
+ {
+ cwd: process.cwd(),
+ args: nodeArgs,
+ },
+ [root, appName, verbose, originalDirectory, template],
+ `
+ var init = require('${packageName}/scripts/init.js');
+ init.apply(null, JSON.parse(process.argv[1]));
+ `
);
- const init = require(scriptsPath);
- init(root, appName, verbose, originalDirectory, template);
if (version === 'react-scripts@0.9.x') {
console.log(
chalk.yellow(
- `\nNote: the project was boostrapped with an old unsupported version of tools.\n` +
+ `\nNote: the project was bootstrapped with an old unsupported version of tools.\n` +
`Please update to Node >=6 and npm >=3 to get supported tools in new projects.\n`
)
);
@@ -394,14 +439,18 @@ function getInstallPackage(version, originalDirectory) {
const validSemver = semver.valid(version);
if (validSemver) {
packageToInstall += `@${validSemver}`;
- } else if (version && version.match(/^file:/)) {
- packageToInstall = `file:${path.resolve(
- originalDirectory,
- version.match(/^file:(.*)?$/)[1]
- )}`;
} else if (version) {
- // for tar.gz or alternative paths
- packageToInstall = version;
+ if (version[0] === '@' && version.indexOf('/') === -1) {
+ packageToInstall += version;
+ } else if (version.match(/^file:/)) {
+ packageToInstall = `file:${path.resolve(
+ originalDirectory,
+ version.match(/^file:(.*)?$/)[1]
+ )}`;
+ } else {
+ // for tar.gz or alternative paths
+ packageToInstall = version;
+ }
}
return packageToInstall;
}
@@ -523,6 +572,11 @@ function checkNodeVersion(packageName) {
packageName,
'package.json'
);
+
+ if (!fs.existsSync(packageJsonPath)) {
+ return;
+ }
+
const packageJson = require(packageJsonPath);
if (!packageJson.engines || !packageJson.engines.node) {
return;
@@ -777,3 +831,23 @@ function checkIfOnline(useYarn) {
});
});
}
+
+function executeNodeScript({ cwd, args }, data, source) {
+ return new Promise((resolve, reject) => {
+ const child = spawn(
+ process.execPath,
+ [...args, '-e', source, '--', JSON.stringify(data)],
+ { cwd, stdio: 'inherit' }
+ );
+
+ child.on('close', code => {
+ if (code !== 0) {
+ reject({
+ command: `node ${args.join(' ')}`,
+ });
+ return;
+ }
+ resolve();
+ });
+ });
+}
diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js
index a093636609e..808ebcfe69c 100755
--- a/packages/create-react-app/index.js
+++ b/packages/create-react-app/index.js
@@ -42,13 +42,13 @@ var currentNodeVersion = process.versions.node;
var semver = currentNodeVersion.split('.');
var major = semver[0];
-if (major < 4) {
+if (major < 8) {
console.error(
chalk.red(
'You are running Node ' +
currentNodeVersion +
'.\n' +
- 'Create React App requires Node 4 or higher. \n' +
+ 'Create React App requires Node 8 or higher. \n' +
'Please update your version of Node.'
)
);
diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json
index 86bd258f736..18c098e0b81 100644
--- a/packages/create-react-app/package.json
+++ b/packages/create-react-app/package.json
@@ -1,6 +1,6 @@
{
"name": "create-react-app",
- "version": "1.5.1",
+ "version": "2.0.1",
"keywords": [
"react"
],
@@ -21,16 +21,15 @@
"create-react-app": "./index.js"
},
"dependencies": {
- "chalk": "^1.1.3",
- "commander": "^2.9.0",
- "cross-spawn": "^4.0.0",
- "envinfo": "3.4.2",
- "fs-extra": "^5.0.0",
- "hyperquest": "^2.1.2",
- "react-dev-utils": "^5.0.0",
- "semver": "^5.0.3",
- "tar-pack": "^3.4.0",
+ "chalk": "1.1.3",
+ "commander": "2.18.0",
+ "cross-spawn": "4.0.2",
+ "envinfo": "5.10.0",
+ "fs-extra": "5.0.0",
+ "hyperquest": "2.1.3",
+ "semver": "5.5.1",
+ "tar-pack": "3.4.1",
"tmp": "0.0.33",
- "validate-npm-package-name": "^3.0.0"
+ "validate-npm-package-name": "3.0.0"
}
}
diff --git a/packages/eslint-config-react-app/LICENSE b/packages/eslint-config-react-app/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/eslint-config-react-app/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/eslint-config-react-app/README.md b/packages/eslint-config-react-app/README.md
index b85a69bb5ac..1cf00fb1564 100644
--- a/packages/eslint-config-react-app/README.md
+++ b/packages/eslint-config-react-app/README.md
@@ -19,7 +19,7 @@ If you want to use this ESLint configuration in a project not built with Create
First, install this package, ESLint and the necessary plugins.
```sh
- npm install --save-dev eslint-config-react-app babel-eslint@^7.2.3 eslint@^4.1.1 eslint-plugin-flowtype@^2.34.1 eslint-plugin-import@^2.6.0 eslint-plugin-jsx-a11y@^5.1.1 eslint-plugin-react@^7.1.0
+ npm install --save-dev eslint-config-react-app babel-eslint@^9.0.0 eslint@^5.0.0 eslint-plugin-flowtype@^2.34.1 eslint-plugin-import@^2.6.0 eslint-plugin-jsx-a11y@^6.0.0 eslint-plugin-react@^7.1.0
```
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 7d2e716956a..92b65234a81 100644
--- a/packages/eslint-config-react-app/index.js
+++ b/packages/eslint-config-react-app/index.js
@@ -39,12 +39,10 @@ module.exports = {
},
parserOptions: {
- ecmaVersion: 6,
+ ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
- generators: true,
- experimentalObjectRestSpread: true,
},
},
@@ -53,7 +51,7 @@ module.exports = {
'array-callback-return': 'warn',
'default-case': ['warn', { commentPattern: '^no default$' }],
'dot-location': ['warn', 'property'],
- eqeqeq: ['warn', 'allow-null'],
+ eqeqeq: ['warn', 'smart'],
'new-parens': 'warn',
'no-array-constructor': 'warn',
'no-caller': 'warn',
@@ -201,7 +199,10 @@ module.exports = {
'react/jsx-uses-react': 'warn',
'react/jsx-uses-vars': 'warn',
'react/no-danger-with-children': 'warn',
- 'react/no-deprecated': 'warn',
+ // Disabled because of undesirable warnings
+ // See https://github.com/facebook/create-react-app/issues/5204 for
+ // blockers until its re-enabled
+ // 'react/no-deprecated': 'warn',
'react/no-direct-mutation-state': 'warn',
'react/no-is-mounted': 'warn',
'react/react-in-jsx-scope': 'error',
diff --git a/packages/eslint-config-react-app/package.json b/packages/eslint-config-react-app/package.json
index c4b51925e69..e3ed2213f54 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": "2.1.0",
+ "version": "3.0.2",
"description": "ESLint configuration used by Create React App",
"repository": "facebook/create-react-app",
"license": "MIT",
@@ -11,14 +11,14 @@
"index.js"
],
"peerDependencies": {
- "babel-eslint": "^8.2.2",
- "eslint": "^4.1.1",
- "eslint-plugin-flowtype": "^2.34.1",
- "eslint-plugin-import": "^2.6.0",
- "eslint-plugin-jsx-a11y": "^6.0.2",
- "eslint-plugin-react": "^7.7.0"
+ "babel-eslint": "9.x",
+ "eslint": "5.x",
+ "eslint-plugin-flowtype": "2.x",
+ "eslint-plugin-import": "2.x",
+ "eslint-plugin-jsx-a11y": "6.x",
+ "eslint-plugin-react": "7.x"
},
"dependencies": {
- "confusing-browser-globals": "^1.0.0"
+ "confusing-browser-globals": "^1.0.2"
}
}
diff --git a/packages/react-app-polyfill/LICENSE b/packages/react-app-polyfill/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/react-app-polyfill/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/react-app-polyfill/README.md b/packages/react-app-polyfill/README.md
new file mode 100644
index 00000000000..3243ad7b16e
--- /dev/null
+++ b/packages/react-app-polyfill/README.md
@@ -0,0 +1,48 @@
+# react-app-polyfill
+
+This package includes polyfills for various browsers.
+It includes minimum requirements and commonly used language features used by [Create React App](https://github.com/facebook/create-react-app) projects.
+
+### Features
+
+Each polyfill ensures the following language features are present:
+
+1. `Promise` (for `async` / `await` support)
+1. `window.fetch` (a Promise-based way to make web requests in the browser)
+1. `Object.assign` (a helper required for Object Spread, i.e. `{ ...a, ...b }`)
+1. `Symbol` (a built-in object used by `for...of` syntax and friends)
+1. `Array.from` (a built-in static method used by array spread, i.e. `[...arr]`)
+
+### Usage
+
+First, install the package using Yarn or npm:
+
+```bash
+npm install react-app-polyfill
+```
+
+or
+
+```bash
+yarn add react-app-polyfill
+```
+
+Now, you can import the entry point for the minimal version you intend to support. For example, if you import the IE9 entry point, this will include IE10 and IE11 support.
+
+#### Internet Explorer 9
+
+```js
+// This must be the first line in src/index.js
+import 'react-app-polyfill/ie9';
+
+// ...
+```
+
+#### Internet Explorer 11
+
+```js
+// This must be the first line in src/index.js
+import 'react-app-polyfill/ie11';
+
+// ...
+```
diff --git a/packages/react-scripts/config/polyfills.js b/packages/react-app-polyfill/ie11.js
similarity index 74%
rename from packages/react-scripts/config/polyfills.js
rename to packages/react-app-polyfill/ie11.js
index 8d97fb4ac39..15a7a9a085b 100644
--- a/packages/react-scripts/config/polyfills.js
+++ b/packages/react-app-polyfill/ie11.js
@@ -1,11 +1,9 @@
-// @remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
-// @remove-on-eject-end
'use strict';
if (typeof Promise === 'undefined') {
@@ -23,8 +21,7 @@ require('whatwg-fetch');
// It will use the native implementation if it's present and isn't buggy.
Object.assign = require('object-assign');
-// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
-// We don't polyfill it in the browser--this is user's responsibility.
-if (process.env.NODE_ENV === 'test') {
- require('raf').polyfill(global);
-}
+// Support for...of (a commonly used syntax feature that requires Symbols)
+require('core-js/es6/symbol');
+// Support iterable spread (...Set, ...Map)
+require('core-js/fn/array/from');
diff --git a/packages/react-error-overlay/src/utils/pollyfills.js b/packages/react-app-polyfill/ie9.js
similarity index 63%
rename from packages/react-error-overlay/src/utils/pollyfills.js
rename to packages/react-app-polyfill/ie9.js
index ddd5aeb9651..ef89d14fa40 100644
--- a/packages/react-error-overlay/src/utils/pollyfills.js
+++ b/packages/react-app-polyfill/ie9.js
@@ -4,6 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+'use strict';
if (typeof Promise === 'undefined') {
// Rejection tracking prevents a common issue where React gets into an
@@ -13,6 +14,19 @@ if (typeof Promise === 'undefined') {
window.Promise = require('promise/lib/es6-extensions.js');
}
+// fetch() polyfill for making API calls.
+require('whatwg-fetch');
+
// Object.assign() is commonly used with React.
// It will use the native implementation if it's present and isn't buggy.
Object.assign = require('object-assign');
+
+// Support for...of (a commonly used syntax feature that requires Symbols)
+require('core-js/es6/symbol');
+// Support iterable spread (...Set, ...Map)
+require('core-js/fn/array/from');
+
+// React 16+ relies on Map, Set, and requestAnimationFrame
+require('core-js/es6/map');
+require('core-js/es6/set');
+require('raf').polyfill(window);
diff --git a/packages/react-app-polyfill/jsdom.js b/packages/react-app-polyfill/jsdom.js
new file mode 100644
index 00000000000..1fa4c99623c
--- /dev/null
+++ b/packages/react-app-polyfill/jsdom.js
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+// Make sure we're in a Browser-like environment before importing polyfills
+// This prevents `fetch()` from being imported in a Node test environment
+if (typeof window !== 'undefined') {
+ // fetch() polyfill for making API calls.
+ require('whatwg-fetch');
+}
diff --git a/packages/react-app-polyfill/package.json b/packages/react-app-polyfill/package.json
new file mode 100644
index 00000000000..206239e7b0e
--- /dev/null
+++ b/packages/react-app-polyfill/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "react-app-polyfill",
+ "version": "0.1.2",
+ "description": "Polyfills for various browsers including commonly used language features",
+ "repository": "facebook/create-react-app",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/facebook/create-react-app/issues"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "files": [
+ "ie9.js",
+ "ie11.js",
+ "jsdom.js"
+ ],
+ "dependencies": {
+ "core-js": "2.5.7",
+ "object-assign": "4.1.1",
+ "promise": "8.0.2",
+ "raf": "3.4.0",
+ "whatwg-fetch": "3.0.0"
+ }
+}
diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js
index 68aae411f12..c718e6e3823 100644
--- a/packages/react-dev-utils/FileSizeReporter.js
+++ b/packages/react-dev-utils/FileSizeReporter.js
@@ -15,6 +15,14 @@ var recursive = require('recursive-readdir');
var stripAnsi = require('strip-ansi');
var gzipSize = require('gzip-size').sync;
+function canReadAsset(asset) {
+ return (
+ /\.(js|css)$/.test(asset.name) &&
+ !/service-worker\.js/.test(asset.name) &&
+ !/precache-manifest\.[0-9a-f]+\.js/.test(asset.name)
+ );
+}
+
// Prints a detailed summary of build files.
function printFileSizesAfterBuild(
webpackStats,
@@ -28,8 +36,8 @@ function printFileSizesAfterBuild(
var assets = (webpackStats.stats || [webpackStats])
.map(stats =>
stats
- .toJson()
- .assets.filter(asset => /\.(js|css)$/.test(asset.name))
+ .toJson({ all: false, assets: true })
+ .assets.filter(canReadAsset)
.map(asset => {
var fileContents = fs.readFileSync(path.join(root, asset.name));
var size = gzipSize(fileContents);
@@ -43,7 +51,7 @@ function printFileSizesAfterBuild(
name: path.basename(asset.name),
size: size,
sizeLabel:
- filesize(size) + (difference ? ' (' + difference + ')' : '')
+ filesize(size) + (difference ? ' (' + difference + ')' : ''),
};
})
)
@@ -98,6 +106,7 @@ function printFileSizesAfterBuild(
function removeFileNameHash(buildFolder, fileName) {
return fileName
.replace(buildFolder, '')
+ .replace(/\\/g, '/')
.replace(
/\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/,
(match, p1, p2, p3, p4) => p1 + p4
@@ -126,14 +135,12 @@ function measureFileSizesBeforeBuild(buildFolder) {
recursive(buildFolder, (err, fileNames) => {
var sizes;
if (!err && fileNames) {
- sizes = fileNames
- .filter(fileName => /\.(js|css)$/.test(fileName))
- .reduce((memo, fileName) => {
- var contents = fs.readFileSync(fileName);
- var key = removeFileNameHash(buildFolder, fileName);
- memo[key] = gzipSize(contents);
- return memo;
- }, {});
+ sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
+ var contents = fs.readFileSync(fileName);
+ var key = removeFileNameHash(buildFolder, fileName);
+ memo[key] = gzipSize(contents);
+ return memo;
+ }, {});
}
resolve({
root: buildFolder,
diff --git a/packages/react-dev-utils/InlineChunkHtmlPlugin.js b/packages/react-dev-utils/InlineChunkHtmlPlugin.js
new file mode 100644
index 00000000000..e38ed53d3fb
--- /dev/null
+++ b/packages/react-dev-utils/InlineChunkHtmlPlugin.js
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+class InlineChunkHtmlPlugin {
+ constructor(htmlWebpackPlugin, tests) {
+ this.htmlWebpackPlugin = htmlWebpackPlugin;
+ this.tests = tests;
+ }
+
+ getInlinedTag(publicPath, assets, tag) {
+ if (tag.tagName !== 'script' || !(tag.attributes && tag.attributes.src)) {
+ return tag;
+ }
+ const scriptName = tag.attributes.src.replace(publicPath, '');
+ if (!this.tests.some(test => scriptName.match(test))) {
+ return tag;
+ }
+ const asset = assets[scriptName];
+ if (asset == null) {
+ return tag;
+ }
+ return { tagName: 'script', innerHTML: asset.source(), closeTag: true };
+ }
+
+ apply(compiler) {
+ let publicPath = compiler.options.output.publicPath;
+ if (!publicPath.endsWith('/')) {
+ publicPath += '/';
+ }
+
+ compiler.hooks.compilation.tap('InlineChunkHtmlPlugin', compilation => {
+ const tagFunction = tag =>
+ this.getInlinedTag(publicPath, compilation.assets, tag);
+
+ const hooks = this.htmlWebpackPlugin.getHooks(compilation);
+ hooks.alterAssetTagGroups.tap('InlineChunkHtmlPlugin', assets => {
+ assets.headTags = assets.headTags.map(tagFunction);
+ assets.bodyTags = assets.bodyTags.map(tagFunction);
+ });
+
+ // Still emit the runtime chunk for users who do not use our generated
+ // index.html file.
+ // hooks.afterEmit.tap('InlineChunkHtmlPlugin', () => {
+ // Object.keys(compilation.assets).forEach(assetName => {
+ // if (this.tests.some(test => assetName.match(test))) {
+ // delete compilation.assets[assetName];
+ // }
+ // });
+ // });
+ });
+ }
+}
+
+module.exports = InlineChunkHtmlPlugin;
diff --git a/packages/react-dev-utils/InterpolateHtmlPlugin.js b/packages/react-dev-utils/InterpolateHtmlPlugin.js
index 9233bdefa3c..30438f0b8e0 100644
--- a/packages/react-dev-utils/InterpolateHtmlPlugin.js
+++ b/packages/react-dev-utils/InterpolateHtmlPlugin.js
@@ -6,7 +6,7 @@
*/
// This Webpack plugin lets us interpolate custom variables into `index.html`.
-// Usage: `new InterpolateHtmlPlugin({ 'MY_VARIABLE': 42 })`
+// Usage: `new InterpolateHtmlPlugin(HtmlWebpackPlugin, { 'MY_VARIABLE': 42 })`
// Then, you can use %MY_VARIABLE% in your `index.html`.
// It works in tandem with HtmlWebpackPlugin.
@@ -17,15 +17,16 @@
const escapeStringRegexp = require('escape-string-regexp');
class InterpolateHtmlPlugin {
- constructor(replacements) {
+ constructor(htmlWebpackPlugin, replacements) {
+ this.htmlWebpackPlugin = htmlWebpackPlugin;
this.replacements = replacements;
}
apply(compiler) {
- compiler.plugin('compilation', compilation => {
- compilation.plugin(
- 'html-webpack-plugin-before-html-processing',
- (data, callback) => {
+ compiler.hooks.compilation.tap('InterpolateHtmlPlugin', compilation => {
+ this.htmlWebpackPlugin
+ .getHooks(compilation)
+ .beforeEmit.tap('InterpolateHtmlPlugin', data => {
// Run HTML through a series of user-specified string replacements.
Object.keys(this.replacements).forEach(key => {
const value = this.replacements[key];
@@ -34,9 +35,7 @@ class InterpolateHtmlPlugin {
value
);
});
- callback(null, data);
- }
- );
+ });
});
}
}
diff --git a/packages/react-dev-utils/LICENSE b/packages/react-dev-utils/LICENSE
new file mode 100644
index 00000000000..188fb2b0bd8
--- /dev/null
+++ b/packages/react-dev-utils/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-present, Facebook, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/react-dev-utils/ModuleNotFoundPlugin.js b/packages/react-dev-utils/ModuleNotFoundPlugin.js
new file mode 100644
index 00000000000..55705fd3ea9
--- /dev/null
+++ b/packages/react-dev-utils/ModuleNotFoundPlugin.js
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+const chalk = require('chalk');
+const findUp = require('find-up');
+const path = require('path');
+
+class ModuleNotFoundPlugin {
+ constructor(appPath, yarnLockFile) {
+ this.appPath = appPath;
+ this.yarnLockFile = yarnLockFile;
+
+ this.useYarnCommand = this.useYarnCommand.bind(this);
+ this.getRelativePath = this.getRelativePath.bind(this);
+ this.prettierError = this.prettierError.bind(this);
+ }
+
+ useYarnCommand() {
+ try {
+ return findUp.sync('yarn.lock', { cwd: this.appPath }) != null;
+ } catch (_) {
+ return false;
+ }
+ }
+
+ getRelativePath(_file) {
+ let file = path.relative(this.appPath, _file);
+ if (file.startsWith('..')) {
+ file = _file;
+ } else if (!file.startsWith('.')) {
+ file = '.' + path.sep + file;
+ }
+ return file;
+ }
+
+ prettierError(err) {
+ let { details: _details = '', origin } = err;
+
+ if (origin == null) {
+ const caseSensitivity =
+ err.message &&
+ /\[CaseSensitivePathsPlugin\] `(.*?)` .* `(.*?)`/.exec(err.message);
+ if (caseSensitivity) {
+ const [, incorrectPath, actualName] = caseSensitivity;
+ const actualFile = this.getRelativePath(
+ path.join(path.dirname(incorrectPath), actualName)
+ );
+ const incorrectName = path.basename(incorrectPath);
+ err.message = `Cannot find file: '${incorrectName}' does not match the corresponding name on disk: '${actualFile}'.`;
+ }
+ return err;
+ }
+
+ const file = this.getRelativePath(origin.resource);
+ let details = _details.split('\n');
+
+ const request = /resolve '(.*?)' in '(.*?)'/.exec(details);
+ if (request) {
+ const isModule = details[1] && details[1].includes('module');
+ const isFile = details[1] && details[1].includes('file');
+
+ let [, target, context] = request;
+ context = this.getRelativePath(context);
+ if (isModule) {
+ const isYarn = this.useYarnCommand();
+ details = [
+ `Cannot find module: '${target}'. Make sure this package is installed.`,
+ '',
+ 'You can install this package by running: ' +
+ (isYarn
+ ? chalk.bold(`yarn add ${target}`)
+ : chalk.bold(`npm install ${target}`)) +
+ '.',
+ ];
+ } else if (isFile) {
+ details = [`Cannot find file '${target}' in '${context}'.`];
+ } else {
+ details = [err.message];
+ }
+ } else {
+ details = [err.message];
+ }
+ err.message = [file, ...details].join('\n').replace('Error: ', '');
+
+ const isModuleScopePluginError =
+ err.error && err.error.__module_scope_plugin;
+ if (isModuleScopePluginError) {
+ err.message = err.message.replace('Module not found: ', '');
+ }
+ return err;
+ }
+
+ apply(compiler) {
+ const { prettierError } = this;
+ compiler.hooks.make.intercept({
+ register(tap) {
+ if (
+ !(tap.name === 'MultiEntryPlugin' || tap.name === 'SingleEntryPlugin')
+ ) {
+ return tap;
+ }
+ return Object.assign({}, tap, {
+ fn: (compilation, callback) => {
+ tap.fn(compilation, (err, ...args) => {
+ if (err && err.name === 'ModuleNotFoundError') {
+ err = prettierError(err);
+ }
+ callback(err, ...args);
+ });
+ },
+ });
+ },
+ });
+ compiler.hooks.normalModuleFactory.tap('ModuleNotFoundPlugin', nmf => {
+ nmf.hooks.afterResolve.intercept({
+ register(tap) {
+ if (tap.name !== 'CaseSensitivePathsPlugin') {
+ return tap;
+ }
+ return Object.assign({}, tap, {
+ fn: (compilation, callback) => {
+ tap.fn(compilation, (err, ...args) => {
+ if (
+ err &&
+ err.message &&
+ err.message.includes('CaseSensitivePathsPlugin')
+ ) {
+ err = prettierError(err);
+ }
+ callback(err, ...args);
+ });
+ },
+ });
+ },
+ });
+ });
+ }
+}
+
+module.exports = ModuleNotFoundPlugin;
diff --git a/packages/react-dev-utils/ModuleScopePlugin.js b/packages/react-dev-utils/ModuleScopePlugin.js
index 695426331b9..e84d2b38aab 100644
--- a/packages/react-dev-utils/ModuleScopePlugin.js
+++ b/packages/react-dev-utils/ModuleScopePlugin.js
@@ -9,6 +9,7 @@
const chalk = require('chalk');
const path = require('path');
+const os = require('os');
class ModuleScopePlugin {
constructor(appSrc, allowedFiles = []) {
@@ -18,51 +19,52 @@ class ModuleScopePlugin {
apply(resolver) {
const { appSrcs } = this;
- resolver.plugin('file', (request, callback) => {
- // Unknown issuer, probably webpack internals
- if (!request.context.issuer) {
- return callback();
- }
- if (
- // If this resolves to a node_module, we don't care what happens next
- request.descriptionFileRoot.indexOf('/node_modules/') !== -1 ||
- request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 ||
- // Make sure this request was manual
- !request.__innerRequest_request
- ) {
- return callback();
- }
- // Resolve the issuer from our appSrc and make sure it's one of our files
- // Maybe an indexOf === 0 would be better?
- if (
- appSrcs.every(appSrc => {
- const relative = path.relative(appSrc, request.context.issuer);
- // If it's not in one of our app src or a subdirectory, not our request!
- return relative.startsWith('../') || relative.startsWith('..\\');
- })
- ) {
- return callback();
- }
- const requestFullPath = path.resolve(
- path.dirname(request.context.issuer),
- request.__innerRequest_request
- );
- if (this.allowedFiles.has(requestFullPath)) {
- return callback();
- }
- // Find path from src to the requested file
- // Error if in a parent directory of all given appSrcs
- if (
- appSrcs.every(appSrc => {
- const requestRelative = path.relative(appSrc, requestFullPath);
- return (
- requestRelative.startsWith('../') ||
- requestRelative.startsWith('..\\')
- );
- })
- ) {
- callback(
- new Error(
+ resolver.hooks.file.tapAsync(
+ 'ModuleScopePlugin',
+ (request, contextResolver, callback) => {
+ // Unknown issuer, probably webpack internals
+ if (!request.context.issuer) {
+ return callback();
+ }
+ if (
+ // If this resolves to a node_module, we don't care what happens next
+ request.descriptionFileRoot.indexOf('/node_modules/') !== -1 ||
+ request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 ||
+ // Make sure this request was manual
+ !request.__innerRequest_request
+ ) {
+ return callback();
+ }
+ // Resolve the issuer from our appSrc and make sure it's one of our files
+ // Maybe an indexOf === 0 would be better?
+ if (
+ appSrcs.every(appSrc => {
+ const relative = path.relative(appSrc, request.context.issuer);
+ // If it's not in one of our app src or a subdirectory, not our request!
+ return relative.startsWith('../') || relative.startsWith('..\\');
+ })
+ ) {
+ return callback();
+ }
+ const requestFullPath = path.resolve(
+ path.dirname(request.context.issuer),
+ request.__innerRequest_request
+ );
+ if (this.allowedFiles.has(requestFullPath)) {
+ return callback();
+ }
+ // Find path from src to the requested file
+ // Error if in a parent directory of all given appSrcs
+ if (
+ appSrcs.every(appSrc => {
+ const requestRelative = path.relative(appSrc, requestFullPath);
+ return (
+ requestRelative.startsWith('../') ||
+ requestRelative.startsWith('..\\')
+ );
+ })
+ ) {
+ const scopeError = new Error(
`You attempted to import ${chalk.cyan(
request.__innerRequest_request
)} which falls outside of the project ${chalk.cyan(
@@ -70,19 +72,25 @@ class ModuleScopePlugin {
)} directory. ` +
`Relative imports outside of ${chalk.cyan(
'src/'
- )} are not supported. ` +
+ )} are not supported.` +
+ os.EOL +
`You can either move it inside ${chalk.cyan(
'src/'
)}, or add a symlink to it from project's ${chalk.cyan(
'node_modules/'
)}.`
- ),
- request
- );
- } else {
- callback();
+ );
+ Object.defineProperty(scopeError, '__module_scope_plugin', {
+ value: true,
+ writable: false,
+ enumerable: false,
+ });
+ callback(scopeError, request);
+ } else {
+ callback();
+ }
}
- });
+ );
}
}
diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md
index f63febe7c65..d615f66b5cf 100644
--- a/packages/react-dev-utils/README.md
+++ b/packages/react-dev-utils/README.md
@@ -3,8 +3,8 @@
This package includes some utilities used by [Create React App](https://github.com/facebook/create-react-app).
Please refer to its documentation:
-* [Getting Started](https://github.com/facebook/create-react-app/blob/master/README.md#getting-started) – How to create a new app.
-* [User Guide](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md) – How to develop apps bootstrapped with Create React App.
+- [Getting Started](https://github.com/facebook/create-react-app/blob/master/README.md#getting-started) – How to create a new app.
+- [User Guide](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md) – How to develop apps bootstrapped with Create React App.
## Usage in Create React App Projects
@@ -18,14 +18,14 @@ If you don’t use Create React App, or if you [ejected](https://github.com/face
There is no single entry point. You can only import individual top-level modules.
-#### `new InterpolateHtmlPlugin(replacements: {[key:string]: string})`
+#### `new InterpolateHtmlPlugin(htmlWebpackPlugin: HtmlWebpackPlugin, 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-webpack-plugin) 2.x via its [events](https://github.com/ampedandwired/html-webpack-plugin#events).
```js
var path = require('path');
-var HtmlWebpackPlugin = require('html-dev-plugin');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
// Webpack config
@@ -34,29 +34,61 @@ var publicUrl = '/my-custom-url';
module.exports = {
output: {
// ...
- publicPath: publicUrl + '/'
+ publicPath: publicUrl + '/',
},
// ...
plugins: [
+ // Generates an `index.html` file with the