diff --git a/.travis.yml b/.travis.yml
index 75383b8780..ec44c3294d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,3 +9,6 @@ cache:
- packages/create-react-app/node_modules
- packages/react-scripts/node_modules
script: tasks/e2e.sh
+env:
+ - USE_YARN=no
+ - USE_YARN=yes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bc0f7aa003..0cb0ed88a5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -68,7 +68,7 @@ and then run `npm start` or `npm run build`.
6. Make sure to include “Migrating from ...” instructions for the previous release. Often you can copy and paste them.
7. After merging the changelog update, create a GitHub Release with the same text. See previous Releases for inspiration.
8. **Do not run `npm publish`. Instead, run `npm run publish`.**
-9. Wait for a long time, and it will get published. Don’t worry that it’s stuck. It will bundle dependencies into a single tarball before publishing for faster installs. In the end the publish script will prompt for versions before publishing the packages.
+9. Wait for a long time, and it will get published. Don’t worry that it’s stuck. In the end the publish script will prompt for versions before publishing the packages.
Make sure to test the released version! If you want to be extra careful, you can publish a prerelease by running `npm run publish -- --tag next` instead of `npm run publish`.
diff --git a/README.md b/README.md
index b7e1f1f45d..9daf39241a 100644
--- a/README.md
+++ b/README.md
@@ -109,6 +109,7 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast
- [Post-Processing CSS](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#post-processing-css)
- [Adding Images and Fonts](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-images-and-fonts)
- [Using the `public` Folder](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-the-public-folder)
+- [Using Global Variables](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-global-variables)
- [Adding Bootstrap](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-bootstrap)
- [Adding Flow](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-flow)
- [Adding Custom Environment Variables](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables)
@@ -118,7 +119,9 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast
- [Using HTTPS in Development](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-https-in-development)
- [Generating Dynamic `` Tags on the Server](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#generating-dynamic-meta-tags-on-the-server)
- [Running Tests](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests)
+- [Developing Components in Isolation](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#developing-components-in-isolation)
- [Deployment](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment)
+- [Troubleshooting](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting)
A copy of the user guide will be created as `README.md` in your project folder.
@@ -130,7 +133,7 @@ Please refer to the [User Guide](https://github.com/facebookincubator/create-rea
* **One Dependency:** There is just one build dependency. It uses Webpack, Babel, ESLint, and other amazing projects, but provides a cohesive curated experience on top of them.
-* **Zero Configuration:** There are no configuration files or command line options. Configuring both development and production builds is handled for you so you can focus on writing code.
+* **Convention over Configuration:** You don't need to configure anything by default. Reasonably good configuration of both development and production builds is handled for you so you can focus on writing code.
* **No Lock-In:** You can “eject” to a custom setup at any time. Run a single command, and all the configuration and build dependencies will be moved directly into your project, so you can pick up right where you left off.
@@ -206,6 +209,7 @@ Some of the more popular and actively maintained ones are:
* [mozilla/neo](https://github.com/mozilla/neo)
* [NYTimes/kyt](https://github.com/NYTimes/kyt)
* [zeit/next.js](https://github.com/zeit/next.js)
+* [gatsbyjs/gatsby](https://github.com/gatsbyjs/gatsby)
Notable alternatives also include:
diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md
index c7548b8453..5221c41af5 100644
--- a/packages/babel-preset-react-app/README.md
+++ b/packages/babel-preset-react-app/README.md
@@ -23,3 +23,5 @@ Then create a file named `.babelrc` with following contents in the root folder o
"presets": ["react-app"]
}
```
+
+This preset uses the `useBuiltIns` option with [transform-object-rest-spread](http://babeljs.io/docs/plugins/transform-object-rest-spread/) and [transform-react-jsx](http://babeljs.io/docs/plugins/transform-react-jsx/), which assumes that `Object.assign` is available or polyfilled.
diff --git a/packages/babel-preset-react-app/index.js b/packages/babel-preset-react-app/index.js
index d6e9c4519d..1f7ec0f8e0 100644
--- a/packages/babel-preset-react-app/index.js
+++ b/packages/babel-preset-react-app/index.js
@@ -13,8 +13,16 @@ var path = require('path');
const plugins = [
// class { handleClick = () => { } }
require.resolve('babel-plugin-transform-class-properties'),
+ // The following two plugins use Object.assign directly, instead of Babel's
+ // extends helper. Note that this assumes `Object.assign` is available.
// { ...todo, completed: true }
- require.resolve('babel-plugin-transform-object-rest-spread'),
+ [require.resolve('babel-plugin-transform-object-rest-spread'), {
+ useBuiltIns: true
+ }],
+ // Transforms JSX
+ [require.resolve('babel-plugin-transform-react-jsx'), {
+ useBuiltIns: true
+ }],
// function* () { yield 42; yield 43; }
[require.resolve('babel-plugin-transform-regenerator'), {
// Async functions are converted to generators by babel-preset-latest
@@ -27,15 +35,7 @@ const plugins = [
regenerator: true,
// Resolve the Babel runtime relative to the config.
moduleName: path.dirname(require.resolve('babel-runtime/package'))
- }],
- // The following two plugins are currently necessary to get
- // babel-preset-env to work with rest/spread. More info here:
- // https://github.com/babel/babel-preset-env#caveats
- // https://github.com/babel/babel/issues/4074
- // const { a, ...z } = obj;
- require.resolve('babel-plugin-transform-es2015-destructuring'),
- // const fn = ({ a, ...otherProps }) => otherProps;
- require.resolve('babel-plugin-transform-es2015-parameters')
+ }]
];
// This is similar to how `env` works in Babel:
@@ -54,6 +54,12 @@ if (env !== 'development' && env !== 'test' && env !== 'production') {
}
if (env === 'development' || env === 'test') {
+ // The following two plugins are currently necessary to make React warnings
+ // include more valuable information. They are included here because they are
+ // currently not enabled in babel-preset-react. See the below threads for more info:
+ // https://github.com/babel/babel/issues/4702
+ // https://github.com/babel/babel/pull/3540#issuecomment-228673661
+ // https://github.com/facebookincubator/create-react-app/issues/989
plugins.push.apply(plugins, [
// Adds component stack to warning messages
require.resolve('babel-plugin-transform-react-jsx-source'),
@@ -68,7 +74,7 @@ if (env === 'test') {
// ES features necessary for user's Node version
[require('babel-preset-env').default, {
targets: {
- node: parseFloat(process.versions.node),
+ node: 'current',
},
}],
// JSX, Flow
@@ -99,4 +105,3 @@ if (env === 'test') {
// ]);
}
}
-
diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json
index bba2df1e0b..08a210674e 100644
--- a/packages/babel-preset-react-app/package.json
+++ b/packages/babel-preset-react-app/package.json
@@ -12,15 +12,14 @@
],
"dependencies": {
"babel-plugin-transform-class-properties": "6.16.0",
- "babel-plugin-transform-es2015-destructuring": "6.16.0",
- "babel-plugin-transform-es2015-parameters": "6.17.0",
- "babel-plugin-transform-object-rest-spread": "6.16.0",
+ "babel-plugin-transform-object-rest-spread": "6.19.0",
"babel-plugin-transform-react-constant-elements": "6.9.1",
+ "babel-plugin-transform-react-jsx": "6.8.0",
"babel-plugin-transform-react-jsx-self": "6.11.0",
"babel-plugin-transform-react-jsx-source": "6.9.0",
"babel-plugin-transform-regenerator": "6.16.1",
"babel-plugin-transform-runtime": "6.15.0",
- "babel-preset-env": "0.0.6",
+ "babel-preset-env": "0.0.8",
"babel-preset-latest": "6.16.0",
"babel-preset-react": "6.16.0",
"babel-runtime": "6.11.6"
diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js
index d6478a1354..23d8b5e8de 100644
--- a/packages/create-react-app/index.js
+++ b/packages/create-react-app/index.js
@@ -101,26 +101,54 @@ function createApp(name, verbose, version) {
process.chdir(root);
console.log('Installing packages. This might take a couple minutes.');
- console.log('Installing react-scripts from npm...');
+ console.log('Installing react-scripts...');
console.log();
run(root, appName, version, verbose, originalDirectory);
}
-function run(root, appName, version, verbose, originalDirectory) {
- var installPackage = getInstallPackage(version);
- var packageName = getPackageName(installPackage);
+function install(packageToInstall, verbose, callback) {
var args = [
- 'install',
- verbose && '--verbose',
- '--save-dev',
- '--save-exact',
- installPackage,
- ].filter(function(e) { return e; });
- var proc = spawn('npm', args, {stdio: 'inherit'});
+ 'add',
+ '--dev',
+ '--exact',
+ packageToInstall,
+ ];
+ var proc = spawn('yarn', args, {stdio: 'inherit'});
+
+ var yarnExists = true;
+ proc.on('error', function (err) {
+ if (err.code === 'ENOENT') {
+ yarnExists = false;
+ }
+ });
proc.on('close', function (code) {
+ if (yarnExists) {
+ callback(code, 'yarn', args);
+ return;
+ }
+ // No Yarn installed, continuing with npm.
+ args = [
+ 'install',
+ verbose && '--verbose',
+ '--save-dev',
+ '--save-exact',
+ packageToInstall,
+ ].filter(function(e) { return e; });
+ var npmProc = spawn('npm', args, {stdio: 'inherit'});
+ npmProc.on('close', function (code) {
+ callback(code, 'npm', args);
+ });
+ });
+}
+
+function run(root, appName, version, verbose, originalDirectory) {
+ var packageToInstall = getInstallPackage(version);
+ var packageName = getPackageName(packageToInstall);
+
+ install(packageToInstall, verbose, function (code, command, args) {
if (code !== 0) {
- console.error('`npm ' + args.join(' ') + '` failed');
+ console.error('`' + command + ' ' + args.join(' ') + '` failed');
return;
}
diff --git a/packages/eslint-config-react-app/index.js b/packages/eslint-config-react-app/index.js
index f2f79a2dcc..a067dd091b 100644
--- a/packages/eslint-config-react-app/index.js
+++ b/packages/eslint-config-react-app/index.js
@@ -43,8 +43,7 @@ module.exports = {
settings: {
'import/ignore': [
- 'node_modules',
- '\\.(json|css|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$',
+ 'node_modules'
],
'import/extensions': ['.js'],
'import/resolver': {
diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md
index 14c69493e5..4d1e6ad302 100644
--- a/packages/react-dev-utils/README.md
+++ b/packages/react-dev-utils/README.md
@@ -142,6 +142,22 @@ compiler.plugin('done', function(stats) {
});
```
+#### `getProcessForPort(port: number): string`
+
+Finds the currently running process on `port`.
+Returns a string containing the name and directory, e.g.,
+
+```
+create-react-app
+in /Users/developer/create-react-app
+```
+
+```js
+var getProcessForPort = require('react-dev-utils/getProcessForPort');
+
+getProcessForPort(3000);
+```
+
#### `openBrowser(url: string): boolean`
Attempts to open the browser with a given URL.
diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js
new file mode 100644
index 0000000000..5540fbad47
--- /dev/null
+++ b/packages/react-dev-utils/getProcessForPort.js
@@ -0,0 +1,61 @@
+var chalk = require('chalk');
+var execSync = require('child_process').execSync;
+var path = require('path');
+
+var execOptions = {
+ encoding: 'utf8',
+ stdio: [
+ 'pipe', // stdin (default)
+ 'pipe', // stdout (default)
+ 'ignore' //stderr
+ ]
+};
+
+function isProcessAReactApp(processCommand) {
+ return /^node .*react-scripts\/scripts\/start\.js\s?$/.test(processCommand);
+}
+
+function getProcessIdOnPort(port) {
+ return execSync('lsof -i:' + port + ' -P -t -sTCP:LISTEN', execOptions).trim();
+}
+
+function getPackageNameInDirectory(directory) {
+ var packagePath = path.join(directory.trim(), 'package.json');
+
+ try {
+ return require(packagePath).name;
+ } catch(e) {
+ return null;
+ }
+
+}
+
+function getProcessCommand(processId, processDirectory) {
+ var command = execSync('ps -o command -p ' + processId + ' | sed -n 2p', execOptions);
+
+ if (isProcessAReactApp(command)) {
+ const packageName = getPackageNameInDirectory(processDirectory);
+ return (packageName) ? packageName + '\n' : command;
+ } else {
+ return command;
+ }
+
+}
+
+function getDirectoryOfProcessById(processId) {
+ return execSync('lsof -p '+ processId + ' | grep cwd | awk \'{print $9}\'', execOptions).trim();
+}
+
+function getProcessForPort(port) {
+ try {
+ var processId = getProcessIdOnPort(port);
+ var directory = getDirectoryOfProcessById(processId);
+ var command = getProcessCommand(processId, directory);
+ return chalk.cyan(command) + chalk.blue(' in ') + chalk.cyan(directory);
+ } catch(e) {
+ return null;
+ }
+}
+
+module.exports = getProcessForPort;
+
diff --git a/packages/react-dev-utils/openBrowser.js b/packages/react-dev-utils/openBrowser.js
index 76b33a5924..bee85a7d29 100644
--- a/packages/react-dev-utils/openBrowser.js
+++ b/packages/react-dev-utils/openBrowser.js
@@ -28,7 +28,7 @@ function openBrowser(url) {
// Fallback to opn
// (It will always open new tab)
try {
- opn(url);
+ opn(url).catch(() => {}); // Prevent `unhandledRejection` error.
return true;
} catch (err) {
return false;
diff --git a/packages/react-dev-utils/openChrome.applescript b/packages/react-dev-utils/openChrome.applescript
index 4dfec4a265..b36b70f6cf 100644
--- a/packages/react-dev-utils/openChrome.applescript
+++ b/packages/react-dev-utils/openChrome.applescript
@@ -23,7 +23,7 @@ on run argv
set theTabIndex to 0
repeat with theTab in every tab of theWindow
set theTabIndex to theTabIndex + 1
- if theTab's URL is theURL then
+ if theTab's URL as string contains theURL then
set found to true
exit repeat
end if
@@ -38,6 +38,7 @@ on run argv
tell theTab to reload
set index of theWindow to 1
set theWindow's active tab index to theTabIndex
+ tell theWindow to activate
else
tell window 1
activate
diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json
index 14c860999a..c9f2fac2dc 100644
--- a/packages/react-dev-utils/package.json
+++ b/packages/react-dev-utils/package.json
@@ -14,6 +14,7 @@
"clearConsole.js",
"checkRequiredFiles.js",
"formatWebpackMessages.js",
+ "getProcessForPort.js",
"InterpolateHtmlPlugin.js",
"openChrome.applescript",
"openBrowser.js",
@@ -29,8 +30,5 @@
"opn": "4.0.2",
"sockjs-client": "1.0.3",
"strip-ansi": "3.0.1"
- },
- "peerDependencies": {
- "webpack": "^1.13.2"
}
}
diff --git a/packages/react-scripts/config/jest/transform.js b/packages/react-scripts/config/jest/transform.js
index 11a0149f97..145bd86cc9 100644
--- a/packages/react-scripts/config/jest/transform.js
+++ b/packages/react-scripts/config/jest/transform.js
@@ -9,5 +9,6 @@
const babelJest = require('babel-jest');
module.exports = babelJest.createTransformer({
- presets: [require.resolve('babel-preset-react-app')]
+ presets: [require.resolve('babel-preset-react-app')],
+ babelrc: false
});
diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js
index 1c154c3616..d15b16a054 100644
--- a/packages/react-scripts/config/paths.js
+++ b/packages/react-scripts/config/paths.js
@@ -43,6 +43,7 @@ module.exports = {
appIndexJs: resolveApp('src/index.js'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
+ yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
appNodeModules: resolveApp('node_modules'),
ownNodeModules: resolveApp('node_modules'),
@@ -62,13 +63,13 @@ module.exports = {
appIndexJs: resolveApp('src/index.js'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
+ yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
appNodeModules: resolveApp('node_modules'),
// this is empty with npm3 but node resolution searches higher anyway:
ownNodeModules: resolveOwn('../node_modules'),
nodePaths: nodePaths
};
-// @remove-on-eject-end
// config before publish: we're in ./packages/react-scripts/config/
if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) {
@@ -79,9 +80,11 @@ if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1)
appIndexJs: resolveOwn('../template/src/index.js'),
appPackageJson: resolveOwn('../package.json'),
appSrc: resolveOwn('../template/src'),
+ yarnLockFile: resolveOwn('../template/yarn.lock'),
testsSetup: resolveOwn('../template/src/setupTests.js'),
appNodeModules: resolveOwn('../node_modules'),
ownNodeModules: resolveOwn('../node_modules'),
nodePaths: nodePaths
};
}
+// @remove-on-eject-end
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index d875c63e8d..8e264d4028 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -12,7 +12,6 @@
var path = require('path');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
-var findCacheDir = require('find-cache-dir');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
@@ -34,11 +33,9 @@ var env = getClientEnvironment(publicUrl);
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
- // This makes the bundle appear split into separate modules in the devtools.
- // We don't use source maps here because they can be confusing:
- // https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875
- // You may want 'cheap-module-source-map' instead if you prefer source maps.
- devtool: 'eval',
+ // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
+ // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
+ devtool: 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
@@ -111,6 +108,33 @@ module.exports = {
}
],
loaders: [
+ // Default loader: load all assets that are not handled
+ // by other loaders with the url loader.
+ // Note: This list needs to be updated with every change of extensions
+ // the other loaders match.
+ // E.g., when adding a loader for a new supported file extension,
+ // we need to add the supported extension to this loader too.
+ // Add one new line in `exclude` for each loader.
+ //
+ // "file" loader makes sure those assets get served by WebpackDevServer.
+ // When you `import` an asset, you get its (virtual) filename.
+ // In production, they would get copied to the `build` folder.
+ // "url" loader works like "file" loader except that it embeds assets
+ // smaller than specified limit in bytes as data URLs to avoid requests.
+ // A missing `test` is equivalent to a match.
+ {
+ exclude: [
+ /\.html$/,
+ /\.(js|jsx)$/,
+ /\.css$/,
+ /\.json$/
+ ],
+ loader: 'url',
+ query: {
+ limit: 10000,
+ name: 'static/media/[name].[hash:8].[ext]'
+ }
+ },
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
@@ -122,12 +146,9 @@ module.exports = {
presets: [require.resolve('babel-preset-react-app')],
// @remove-on-eject-end
// This is a feature of `babel-loader` for webpack (not Babel itself).
- // It enables caching results in ./node_modules/.cache/react-scripts/
- // directory for faster rebuilds. We use findCacheDir() because of:
- // https://github.com/facebookincubator/create-react-app/issues/483
- cacheDirectory: findCacheDir({
- name: 'react-scripts'
- })
+ // It enables caching results in ./node_modules/.cache/babel-loader/
+ // directory for faster rebuilds.
+ cacheDirectory: true
}
},
// "postcss" loader applies autoprefixer to our CSS.
@@ -144,26 +165,6 @@ module.exports = {
{
test: /\.json$/,
loader: 'json'
- },
- // "file" loader makes sure those assets get served by WebpackDevServer.
- // When you `import` an asset, you get its (virtual) filename.
- // In production, they would get copied to the `build` folder.
- {
- test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
- loader: 'file',
- query: {
- name: 'static/media/[name].[hash:8].[ext]'
- }
- },
- // "url" loader works just like "file" loader but it also embeds
- // assets smaller than specified size as data URLs to avoid requests.
- {
- test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
- loader: 'url',
- query: {
- limit: 10000,
- name: 'static/media/[name].[hash:8].[ext]'
- }
}
]
},
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index e0141b851b..6001689689 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -116,6 +116,31 @@ module.exports = {
}
],
loaders: [
+ // Default loader: load all assets that are not handled
+ // by other loaders with the url loader.
+ // Note: This list needs to be updated with every change of extensions
+ // the other loaders match.
+ // E.g., when adding a loader for a new supported file extension,
+ // we need to add the supported extension to this loader too.
+ // Add one new line in `exclude` for each loader.
+ //
+ // "file" loader makes sure those assets end up in the `build` folder.
+ // When you `import` an asset, you get its filename.
+ // "url" loader works just like "file" loader but it also embeds
+ // assets smaller than specified size as data URLs to avoid requests.
+ {
+ exclude: [
+ /\.html$/,
+ /\.(js|jsx)$/,
+ /\.css$/,
+ /\.json$/
+ ],
+ loader: 'url',
+ query: {
+ limit: 10000,
+ name: 'static/media/[name].[hash:8].[ext]'
+ }
+ },
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
@@ -142,15 +167,7 @@ module.exports = {
// in the main CSS file.
{
test: /\.css$/,
- // "?-autoprefixer" disables autoprefixer in css-loader itself:
- // https://github.com/webpack/css-loader/issues/281
- // We already have it thanks to postcss. We only pass this flag in
- // production because "css" loader only enables autoprefixer-powered
- // removal of unnecessary prefixes when Uglify plugin is enabled.
- // Webpack 1.x uses Uglify plugin as a signal to minify *all* the assets
- // including CSS. This is confusing and will be removed in Webpack 2:
- // https://github.com/webpack/webpack/issues/283
- loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&-autoprefixer!postcss')
+ loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1!postcss')
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// JSON is not enabled by default in Webpack but both Node and Browserify
@@ -158,25 +175,6 @@ module.exports = {
{
test: /\.json$/,
loader: 'json'
- },
- // "file" loader makes sure those assets end up in the `build` folder.
- // When you `import` an asset, you get its filename.
- {
- test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
- loader: 'file',
- query: {
- name: 'static/media/[name].[hash:8].[ext]'
- }
- },
- // "url" loader works just like "file" loader but it also embeds
- // assets smaller than specified size as data URLs to avoid requests.
- {
- test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
- loader: 'url',
- query: {
- limit: 10000,
- name: 'static/media/[name].[hash:8].[ext]'
- }
}
]
},
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 58bb4ab1d0..d0f22bdd93 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -26,14 +26,14 @@
"autoprefixer": "6.5.1",
"babel-core": "6.17.0",
"babel-eslint": "7.0.0",
- "babel-jest": "16.0.0",
- "babel-loader": "6.2.5",
+ "babel-jest": "17.0.2",
+ "babel-loader": "6.2.7",
"babel-preset-react-app": "^1.0.0",
"case-sensitive-paths-webpack-plugin": "1.1.4",
"chalk": "1.1.3",
"connect-history-api-fallback": "1.3.0",
"cross-spawn": "4.0.2",
- "css-loader": "0.25.0",
+ "css-loader": "0.26.0",
"detect-port": "1.0.1",
"dotenv": "2.0.0",
"eslint": "3.8.1",
@@ -46,12 +46,11 @@
"extract-text-webpack-plugin": "1.0.1",
"file-loader": "0.9.0",
"filesize": "3.3.0",
- "find-cache-dir": "0.1.1",
"fs-extra": "0.30.0",
"gzip-size": "3.0.0",
"html-webpack-plugin": "2.24.0",
"http-proxy-middleware": "0.17.2",
- "jest": "16.0.2",
+ "jest": "17.0.2",
"json-loader": "0.5.4",
"object-assign": "4.1.0",
"path-exists": "2.1.0",
@@ -59,7 +58,6 @@
"promise": "7.1.1",
"react-dev-utils": "^0.3.0",
"recursive-readdir": "2.1.0",
- "rimraf": "2.5.4",
"strip-ansi": "3.0.1",
"style-loader": "0.13.1",
"url-loader": "0.5.7",
@@ -69,57 +67,10 @@
"whatwg-fetch": "1.0.0"
},
"devDependencies": {
- "bundle-deps": "1.0.0",
"react": "^15.3.0",
"react-dom": "^15.3.0"
},
"optionalDependencies": {
"fsevents": "1.0.14"
- },
- "bundledDependencies": [
- "autoprefixer",
- "babel-core",
- "babel-eslint",
- "babel-jest",
- "babel-loader",
- "babel-preset-react-app",
- "case-sensitive-paths-webpack-plugin",
- "chalk",
- "connect-history-api-fallback",
- "cross-spawn",
- "css-loader",
- "detect-port",
- "dotenv",
- "eslint",
- "eslint-config-react-app",
- "eslint-loader",
- "eslint-plugin-flowtype",
- "eslint-plugin-import",
- "eslint-plugin-jsx-a11y",
- "eslint-plugin-react",
- "extract-text-webpack-plugin",
- "file-loader",
- "filesize",
- "find-cache-dir",
- "fs-extra",
- "gzip-size",
- "html-webpack-plugin",
- "http-proxy-middleware",
- "jest",
- "json-loader",
- "object-assign",
- "path-exists",
- "postcss-loader",
- "promise",
- "react-dev-utils",
- "recursive-readdir",
- "rimraf",
- "strip-ansi",
- "style-loader",
- "url-loader",
- "webpack",
- "webpack-dev-server",
- "webpack-manifest-plugin",
- "whatwg-fetch"
- ]
+ }
}
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index d0b92f6a73..8e4141be8b 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -21,9 +21,9 @@ require('dotenv').config({silent: true});
var chalk = require('chalk');
var fs = require('fs-extra');
var path = require('path');
+var pathExists = require('path-exists');
var filesize = require('filesize');
var gzipSize = require('gzip-size').sync;
-var rimrafSync = require('rimraf').sync;
var webpack = require('webpack');
var config = require('../config/webpack.config.prod');
var paths = require('../config/paths');
@@ -31,6 +31,8 @@ var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
var recursive = require('recursive-readdir');
var stripAnsi = require('strip-ansi');
+var useYarn = pathExists.sync(paths.yarnLockFile);
+
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
@@ -75,7 +77,7 @@ recursive(paths.appBuild, (err, fileNames) => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
- rimrafSync(paths.appBuild + '/*');
+ fs.emptyDirSync(paths.appBuild);
// Start the webpack build
build(previousSizeMap);
@@ -161,19 +163,23 @@ function build(previousSizeMap) {
console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.');
console.log('To publish it at ' + chalk.green(homepagePath) + ', run:');
console.log();
- console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages');
+ if (useYarn) {
+ console.log(' ' + chalk.cyan('yarn') + ' add --dev gh-pages');
+ } else {
+ console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages');
+ }
console.log();
console.log('Add the following script in your ' + chalk.cyan('package.json') + '.');
console.log();
console.log(' ' + chalk.dim('// ...'));
console.log(' ' + chalk.yellow('"scripts"') + ': {');
console.log(' ' + chalk.dim('// ...'));
- console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"gh-pages -d build"'));
+ console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"npm run build&&gh-pages -d build"'));
console.log(' }');
console.log();
console.log('Then run:');
console.log();
- console.log(' ' + chalk.cyan('npm') + ' run deploy');
+ console.log(' ' + chalk.cyan(useYarn ? 'yarn' : 'npm') + ' run deploy');
console.log();
} else if (publicPath !== '/') {
// "homepage": "http://mywebsite.com/project"
@@ -200,7 +206,11 @@ function build(previousSizeMap) {
console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.');
console.log('You may also serve it locally with a static server:')
console.log();
- console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server');
+ if (useYarn) {
+ console.log(' ' + chalk.cyan('yarn') + ' global add pushstate-server');
+ } else {
+ console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server');
+ }
console.log(' ' + chalk.cyan('pushstate-server') + ' build');
console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000');
console.log();
diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js
index d14aec6abe..a2bd496102 100644
--- a/packages/react-scripts/scripts/eject.js
+++ b/packages/react-scripts/scripts/eject.js
@@ -8,10 +8,11 @@
*/
var createJestConfig = require('../utils/createJestConfig');
-var fs = require('fs');
+var fs = require('fs-extra');
var path = require('path');
+var pathExists = require('path-exists');
+var paths = require('../config/paths');
var prompt = require('react-dev-utils/prompt');
-var rimrafSync = require('rimraf').sync;
var spawnSync = require('cross-spawn').sync;
var chalk = require('chalk');
var green = chalk.green;
@@ -30,6 +31,25 @@ prompt(
var ownPath = path.join(__dirname, '..');
var appPath = path.join(ownPath, '..', '..');
+
+ function verifyAbsent(file) {
+ if (fs.existsSync(path.join(appPath, file))) {
+ console.error(
+ '`' + file + '` already exists in your app folder. We cannot ' +
+ 'continue as you would lose all the changes in that file or directory. ' +
+ 'Please move or delete it (maybe make a copy for backup) and run this ' +
+ 'command again.'
+ );
+ process.exit(1);
+ }
+ }
+
+ var folders = [
+ 'config',
+ path.join('config', 'jest'),
+ 'scripts'
+ ];
+
var files = [
path.join('config', 'env.js'),
path.join('config', 'paths.js'),
@@ -44,22 +64,13 @@ prompt(
];
// Ensure that the app folder is clean and we won't override any files
- files.forEach(function(file) {
- if (fs.existsSync(path.join(appPath, file))) {
- console.error(
- '`' + file + '` already exists in your app folder. We cannot ' +
- 'continue as you would lose all the changes in that file or directory. ' +
- 'Please delete it (maybe make a copy for backup) and run this ' +
- 'command again.'
- );
- process.exit(1);
- }
- });
+ folders.forEach(verifyAbsent);
+ files.forEach(verifyAbsent);
// Copy the files over
- fs.mkdirSync(path.join(appPath, 'config'));
- fs.mkdirSync(path.join(appPath, 'config', 'jest'));
- fs.mkdirSync(path.join(appPath, 'scripts'));
+ folders.forEach(function(folder) {
+ fs.mkdirSync(path.join(appPath, folder))
+ });
console.log();
console.log(cyan('Copying files into ' + appPath));
@@ -133,9 +144,15 @@ prompt(
);
console.log();
- console.log(cyan('Running npm install...'));
- rimrafSync(ownPath);
- spawnSync('npm', ['install'], {stdio: 'inherit'});
+ if (pathExists.sync(paths.yarnLockFile)) {
+ console.log(cyan('Running yarn...'));
+ fs.removeSync(ownPath);
+ spawnSync('yarn', [], {stdio: 'inherit'});
+ } else {
+ console.log(cyan('Running npm install...'));
+ fs.removeSync(ownPath);
+ spawnSync('npm', ['install'], {stdio: 'inherit'});
+ }
console.log(green('Ejected successfully!'));
console.log();
diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js
index fa42f6dcee..c9a4ea14ac 100644
--- a/packages/react-scripts/scripts/init.js
+++ b/packages/react-scripts/scripts/init.js
@@ -17,6 +17,7 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
var ownPackageName = require(path.join(__dirname, '..', 'package.json')).name;
var ownPath = path.join(appPath, 'node_modules', ownPackageName);
var appPackage = require(path.join(appPath, 'package.json'));
+ var useYarn = pathExists.sync(path.join(appPath, 'yarn.lock'));
// Copy over some of the devDependencies
appPackage.dependencies = appPackage.dependencies || {};
@@ -58,21 +59,31 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
}
});
- // Run another npm install for react and react-dom
- console.log('Installing react and react-dom from npm...');
+ // Run yarn or npm for react and react-dom
+ // TODO: having to do two npm/yarn installs is bad, can we avoid it?
+ var command;
+ var args;
+
+ if (useYarn) {
+ command = 'yarn';
+ args = ['add'];
+ } else {
+ command = 'npm';
+ args = [
+ 'install',
+ '--save',
+ verbose && '--verbose'
+ ].filter(function(e) { return e; });
+ }
+ args.push('react', 'react-dom');
+
+ console.log('Installing react and react-dom using ' + command + '...');
console.log();
- // TODO: having to do two npm installs is bad, can we avoid it?
- var args = [
- 'install',
- 'react',
- 'react-dom',
- '--save',
- verbose && '--verbose'
- ].filter(function(e) { return e; });
- var proc = spawn('npm', args, {stdio: 'inherit'});
+
+ var proc = spawn(command, args, {stdio: 'inherit'});
proc.on('close', function (code) {
if (code !== 0) {
- console.error('`npm ' + args.join(' ') + '` failed');
+ console.error('`' + command + ' ' + args.join(' ') + '` failed');
return;
}
@@ -91,23 +102,23 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
console.log('Success! Created ' + appName + ' at ' + appPath);
console.log('Inside that directory, you can run several commands:');
console.log();
- console.log(chalk.cyan(' npm start'));
+ console.log(chalk.cyan(' ' + command + ' start'));
console.log(' Starts the development server.');
console.log();
- console.log(chalk.cyan(' npm run build'));
+ console.log(chalk.cyan(' ' + command + ' run build'));
console.log(' Bundles the app into static files for production.');
console.log();
- console.log(chalk.cyan(' npm test'));
+ console.log(chalk.cyan(' ' + command + ' test'));
console.log(' Starts the test runner.');
console.log();
- console.log(chalk.cyan(' npm run eject'));
+ console.log(chalk.cyan(' ' + command + ' run eject'));
console.log(' Removes this tool and copies build dependencies, configuration files');
console.log(' and scripts into the app directory. If you do this, you can’t go back!');
console.log();
console.log('We suggest that you begin by typing:');
console.log();
console.log(chalk.cyan(' cd'), cdpath);
- console.log(' ' + chalk.cyan('npm start'));
+ console.log(' ' + chalk.cyan(command + ' start'));
if (readmeExists) {
console.log();
console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`'));
diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js
index 8723c28163..752bd8a08a 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -26,11 +26,17 @@ var detect = require('detect-port');
var clearConsole = require('react-dev-utils/clearConsole');
var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
+var getProcessForPort = require('react-dev-utils/getProcessForPort');
var openBrowser = require('react-dev-utils/openBrowser');
var prompt = require('react-dev-utils/prompt');
+var pathExists = require('path-exists');
var config = require('../config/webpack.config.dev');
var paths = require('../config/paths');
+var useYarn = pathExists.sync(paths.yarnLockFile);
+var cli = useYarn ? 'yarn' : 'npm';
+var isInteractive = process.stdout.isTTY;
+
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
@@ -64,29 +70,42 @@ function setupCompiler(host, port, protocol) {
// bundle, so if you refresh, it'll wait instead of serving the old one.
// "invalid" is short for "bundle invalidated", it doesn't imply any errors.
compiler.plugin('invalid', function() {
- clearConsole();
+ if (isInteractive) {
+ clearConsole();
+ }
console.log('Compiling...');
});
+ var isFirstCompile = true;
+
// "done" event fires when Webpack has finished recompiling the bundle.
// Whether or not you have warnings or errors, you will get this event.
compiler.plugin('done', function(stats) {
- clearConsole();
+ if (isInteractive) {
+ clearConsole();
+ }
// We have switched off the default Webpack output in WebpackDevServer
// options so we are going to "massage" the warnings and errors and present
// them in a readable focused way.
var messages = formatWebpackMessages(stats.toJson({}, true));
- if (!messages.errors.length && !messages.warnings.length) {
+ var isSuccessful = !messages.errors.length && !messages.warnings.length;
+ var showInstructions = isSuccessful && (isInteractive || isFirstCompile);
+
+ if (isSuccessful) {
console.log(chalk.green('Compiled successfully!'));
+ }
+
+ if (showInstructions) {
console.log();
console.log('The app is running at:');
console.log();
console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/'));
console.log();
console.log('Note that the development build is not optimized.');
- console.log('To create a production build, use ' + chalk.cyan('npm run build') + '.');
+ console.log('To create a production build, use ' + chalk.cyan(cli + ' run build') + '.');
console.log();
+ isFirstCompile = false;
}
// If errors exist, only show errors.
@@ -176,18 +195,25 @@ function addMiddleware(devServer) {
// - /sockjs-node/* (WebpackDevServer uses this for hot reloading)
// Tip: use https://jex.im/regulex/ to visualize the regex
var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
- devServer.use(mayProxy,
- // Pass the scope regex both to Express and to the middleware for proxying
- // of both HTTP and WebSockets to work without false positives.
- httpProxyMiddleware(pathname => mayProxy.test(pathname), {
- target: proxy,
- logLevel: 'silent',
- onError: onProxyError(proxy),
- secure: false,
- changeOrigin: true
- })
- );
+
+ // Pass the scope regex both to Express and to the middleware for proxying
+ // of both HTTP and WebSockets to work without false positives.
+ var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), {
+ target: proxy,
+ logLevel: 'silent',
+ onError: onProxyError(proxy),
+ secure: false,
+ changeOrigin: true,
+ ws: true
+ });
+ devServer.use(mayProxy, hpm);
+
+ // Listen for the websocket 'upgrade' event and upgrade the connection.
+ // If this is not done, httpProxyMiddleware will not try to upgrade until
+ // an initial plain HTTP request is made.
+ devServer.listeningApp.on('upgrade', hpm.upgrade);
}
+
// Finally, by now we have certainly resolved the URL.
// It may be /index.html, so let the dev server try serving it again.
devServer.use(devServer.middleware);
@@ -195,6 +221,8 @@ function addMiddleware(devServer) {
function runDevServer(host, port, protocol) {
var devServer = new WebpackDevServer(compiler, {
+ // Enable gzip compression of generated files.
+ compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
@@ -244,10 +272,15 @@ function runDevServer(host, port, protocol) {
return console.log(err);
}
- clearConsole();
+ if (isInteractive) {
+ clearConsole();
+ }
console.log(chalk.cyan('Starting the development server...'));
console.log();
- openBrowser(protocol + '://' + host + ':' + port + '/');
+
+ if (isInteractive) {
+ openBrowser(protocol + '://' + host + ':' + port + '/');
+ }
});
}
@@ -266,14 +299,20 @@ detect(DEFAULT_PORT).then(port => {
return;
}
- clearConsole();
- var question =
- chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') +
- '\n\nWould you like to run the app on another port instead?';
+ if (isInteractive) {
+ clearConsole();
+ var existingProcess = getProcessForPort(DEFAULT_PORT);
+ var question =
+ chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.' +
+ ((existingProcess) ? ' Probably:\n ' + existingProcess : '')) +
+ '\n\nWould you like to run the app on another port instead?';
- prompt(question, true).then(shouldChangePort => {
- if (shouldChangePort) {
- run(port);
- }
- });
+ prompt(question, true).then(shouldChangePort => {
+ if (shouldChangePort) {
+ run(port);
+ }
+ });
+ } else {
+ console.log(chalk.red('Something is already running on port ' + DEFAULT_PORT + '.'));
+ }
});
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index 6f13b12d2e..48409092a2 100644
--- a/packages/react-scripts/template/README.md
+++ b/packages/react-scripts/template/README.md
@@ -20,6 +20,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Post-Processing CSS](#post-processing-css)
- [Adding Images and Fonts](#adding-images-and-fonts)
- [Using the `public` Folder](#using-the-public-folder)
+- [Using Global Variables](#using-global-variables)
- [Adding Bootstrap](#adding-bootstrap)
- [Adding Flow](#adding-flow)
- [Adding Custom Environment Variables](#adding-custom-environment-variables)
@@ -41,6 +42,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Continuous Integration](#continuous-integration)
- [Disabling jsdom](#disabling-jsdom)
- [Experimental Snapshot Testing](#experimental-snapshot-testing)
+- [Developing Components in Isolation](#developing-components-in-isolation)
- [Deployment](#deployment)
- [Building for Relative Paths](#building-for-relative-paths)
- [GitHub Pages](#github-pages)
@@ -48,7 +50,9 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Modulus](#modulus)
- [Netlify](#netlify)
- [Now](#now)
+ - [S3 and CloudFront](#s3-and-cloudfront)
- [Surge](#surge)
+- [Troubleshooting](#troubleshooting)
- [Something Missing?](#something-missing)
## Updating to New Releases
@@ -320,7 +324,7 @@ function Header() {
return
;
}
-export default function Header;
+export default Header;
```
This ensures that when the project is built, Webpack will correctly move the images into the build folder, and provide us with correct paths.
@@ -383,6 +387,22 @@ Keep in mind the downsides of this approach:
However, it can be handy for referencing assets like [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest) from HTML, or including small scripts like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code.
+Note that if you add a `