diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js
index a2d9fdbd86b..0b60510cba6 100755
--- a/packages/create-react-app/createReactApp.js
+++ b/packages/create-react-app/createReactApp.js
@@ -481,7 +481,10 @@ function getPackageName(installPackage) {
);
} else if (installPackage.match(/^file:/)) {
const installPackagePath = installPackage.match(/^file:(.*)?$/)[1];
- const installPackageJson = require(path.join(installPackagePath, 'package.json'));
+ const installPackageJson = require(path.join(
+ installPackagePath,
+ 'package.json'
+ ));
return Promise.resolve(installPackageJson.name);
}
return Promise.resolve(installPackage);
diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js
index 68aae411f12..968e57c3932 100644
--- a/packages/react-dev-utils/FileSizeReporter.js
+++ b/packages/react-dev-utils/FileSizeReporter.js
@@ -43,7 +43,7 @@ function printFileSizesAfterBuild(
name: path.basename(asset.name),
size: size,
sizeLabel:
- filesize(size) + (difference ? ' (' + difference + ')' : '')
+ filesize(size) + (difference ? ' (' + difference + ')' : ''),
};
})
)
diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js
index 4add9f9c1bc..260f73ceea0 100644
--- a/packages/react-dev-utils/WebpackDevServerUtils.js
+++ b/packages/react-dev-utils/WebpackDevServerUtils.js
@@ -34,20 +34,20 @@ if (isSmokeTest) {
};
}
-function prepareUrls(protocol, host, port) {
+function prepareUrls(protocol, host, port, pathname) {
const formatUrl = hostname =>
url.format({
protocol,
hostname,
port,
- pathname: '/',
+ pathname,
});
const prettyPrintUrl = hostname =>
url.format({
protocol,
hostname,
port: chalk.bold(port),
- pathname: '/',
+ pathname,
});
const isUnspecifiedHost = host === '0.0.0.0' || host === '::';
@@ -318,7 +318,7 @@ function prepareProxy(proxy, appPublicFolder) {
// However we also want to respect `proxy` for API calls.
// So if `proxy` is specified as a string, we need to decide which fallback to use.
// We use a heuristic: We want to proxy all the requests that are not meant
- // for static assets and as all the requests for static assets will be using
+ // for static assets and as all the requests for static assets will be using
// `GET` method, we can proxy all non-`GET` requests.
// For `GET` requests, if request `accept`s text/html, we pick /index.html.
// Modern browsers include text/html into `accept` header when navigating.
diff --git a/packages/react-dev-utils/clearConsole.js b/packages/react-dev-utils/clearConsole.js
index cb02af89237..2099a57ec35 100644
--- a/packages/react-dev-utils/clearConsole.js
+++ b/packages/react-dev-utils/clearConsole.js
@@ -8,7 +8,9 @@
'use strict';
function clearConsole() {
- process.stdout.write(process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H');
+ process.stdout.write(
+ process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'
+ );
}
module.exports = clearConsole;
diff --git a/packages/react-dev-utils/errorOverlayMiddleware.js b/packages/react-dev-utils/errorOverlayMiddleware.js
index 873b1994732..57a6003e45d 100644
--- a/packages/react-dev-utils/errorOverlayMiddleware.js
+++ b/packages/react-dev-utils/errorOverlayMiddleware.js
@@ -9,9 +9,9 @@
const launchEditor = require('./launchEditor');
const launchEditorEndpoint = require('./launchEditorEndpoint');
-module.exports = function createLaunchEditorMiddleware() {
+module.exports = function createLaunchEditorMiddleware(servedPathPathname) {
return function launchEditorMiddleware(req, res, next) {
- if (req.url.startsWith(launchEditorEndpoint)) {
+ if (req.url.startsWith(`${servedPathPathname}${launchEditorEndpoint}`)) {
const lineNumber = parseInt(req.query.lineNumber, 10) || 1;
const colNumber = parseInt(req.query.colNumber, 10) || 1;
launchEditor(req.query.fileName, lineNumber, colNumber);
diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js
index f9eda7752b4..8df45464977 100644
--- a/packages/react-dev-utils/getProcessForPort.js
+++ b/packages/react-dev-utils/getProcessForPort.js
@@ -58,7 +58,9 @@ function getProcessCommand(processId, processDirectory) {
function getDirectoryOfProcessById(processId) {
return execSync(
- 'lsof -p ' + processId + ' | awk \'$4=="cwd" {for (i=9; i<=NF; i++) printf "%s ", $i}\'',
+ 'lsof -p ' +
+ processId +
+ ' | awk \'$4=="cwd" {for (i=9; i<=NF; i++) printf "%s ", $i}\'',
execOptions
).trim();
}
diff --git a/packages/react-dev-utils/launchEditor.js b/packages/react-dev-utils/launchEditor.js
index cf190b08619..ddddb7930a7 100644
--- a/packages/react-dev-utils/launchEditor.js
+++ b/packages/react-dev-utils/launchEditor.js
@@ -56,8 +56,7 @@ const COMMON_EDITORS_OSX = {
'/Applications/RubyMine.app/Contents/MacOS/rubymine',
'/Applications/WebStorm.app/Contents/MacOS/webstorm':
'/Applications/WebStorm.app/Contents/MacOS/webstorm',
- '/Applications/MacVim.app/Contents/MacOS/MacVim':
- 'mvim',
+ '/Applications/MacVim.app/Contents/MacOS/MacVim': 'mvim',
};
const COMMON_EDITORS_LINUX = {
diff --git a/packages/react-dev-utils/noopServiceWorkerMiddleware.js b/packages/react-dev-utils/noopServiceWorkerMiddleware.js
index 41566dd7fa8..60957e14b81 100644
--- a/packages/react-dev-utils/noopServiceWorkerMiddleware.js
+++ b/packages/react-dev-utils/noopServiceWorkerMiddleware.js
@@ -7,9 +7,11 @@
'use strict';
-module.exports = function createNoopServiceWorkerMiddleware() {
+module.exports = function createNoopServiceWorkerMiddleware(
+ servedPathPathname
+) {
return function noopServiceWorkerMiddleware(req, res, next) {
- if (req.url === '/service-worker.js') {
+ if (req.url === `${servedPathPathname}/service-worker.js`) {
res.setHeader('Content-Type', 'text/javascript');
res.send(
`// This service worker file is effectively a 'no-op' that will reset any
diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json
index 18f37c971e4..88b4957a43f 100644
--- a/packages/react-dev-utils/package.json
+++ b/packages/react-dev-utils/package.json
@@ -17,6 +17,7 @@
"crossSpawn.js",
"eslintFormatter.js",
"errorOverlayMiddleware.js",
+ "serveAppMiddleware.js",
"FileSizeReporter.js",
"printBuildError.js",
"formatWebpackMessages.js",
diff --git a/packages/react-dev-utils/serveAppMiddleware.js b/packages/react-dev-utils/serveAppMiddleware.js
new file mode 100644
index 00000000000..620847c333c
--- /dev/null
+++ b/packages/react-dev-utils/serveAppMiddleware.js
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+'use strict';
+
+module.exports = function createServeAppMiddleware(servedPathPathname) {
+ return function serveAppMiddleware(req, res, next) {
+ if (servedPathPathname.length > 1 && servedPathPathname !== './') {
+ if (req.url.indexOf(servedPathPathname) === -1) {
+ res.redirect(servedPathPathname);
+ } else {
+ next();
+ }
+ } else {
+ next();
+ }
+ };
+};
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index 9f3131b0660..aec8aa067d3 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -10,6 +10,7 @@
const autoprefixer = require('autoprefixer');
const path = require('path');
+const url = require('url');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
@@ -21,15 +22,18 @@ const getClientEnvironment = require('./env');
const paths = require('./paths');
// Webpack uses `publicPath` to determine where the app is being served from.
-// In development, we always serve from the root. This makes config easier.
-const publicPath = '/';
+// In development, we serve from the root by default. Webpack will serve from
+// the relative path of the homepage field if specified.
+let publicPath = url.parse(paths.servedPath).pathname || '';
+if (publicPath === './') {
+ publicPath = publicPath.slice(1);
+}
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
-const publicUrl = '';
+const publicUrl = paths.servedPath.slice(0, -1) + '/static';
// Get environment variables to inject into our app.
-const env = getClientEnvironment(publicUrl);
-
+const env = getClientEnvironment(publicUrl === '.' ? '' : publicUrl);
// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
@@ -70,6 +74,7 @@ module.exports = {
// There are also additional JS chunk files if you use code splitting.
chunkFilename: 'static/js/[name].chunk.js',
// This is the URL that app is served from. We use "/" in development.
+ // If there is a homepage path defined, it will be served from that instead.
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js
index 3c9c6cf0ce7..bcd81bea04a 100644
--- a/packages/react-scripts/config/webpackDevServer.config.js
+++ b/packages/react-scripts/config/webpackDevServer.config.js
@@ -10,13 +10,18 @@
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
+const serveAppMiddleware = require('react-dev-utils/serveAppMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
+const url = require('url');
const config = require('./webpack.config.dev');
const paths = require('./paths');
+const express = require('express');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
+const servedPathPathname = url.parse(paths.servedPath).pathname || '';
+
module.exports = function(proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
@@ -86,18 +91,24 @@ module.exports = function(proxy, allowedHost) {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
+ index: servedPathPathname,
},
public: allowedHost,
proxy,
before(app) {
// This lets us open files from the runtime error overlay.
- app.use(errorOverlayMiddleware());
+ app.use(errorOverlayMiddleware(servedPathPathname));
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
- app.use(noopServiceWorkerMiddleware());
+ app.use(noopServiceWorkerMiddleware(servedPathPathname));
+ app.use(serveAppMiddleware(servedPathPathname));
+ app.use(
+ `${config.output.publicPath.slice(0, -1)}/static`,
+ express.static(paths.appPublic)
+ );
},
};
};
diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js
index 3ff1b91f435..d8a831d0c0b 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -23,6 +23,7 @@ process.on('unhandledRejection', err => {
require('../config/env');
const fs = require('fs');
+const url = require('url');
const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
@@ -51,6 +52,8 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
+const servedPathPathname = url.parse(paths.servedPath).pathname || '';
+
if (process.env.HOST) {
console.log(
chalk.cyan(
@@ -76,7 +79,7 @@ choosePort(HOST, DEFAULT_PORT)
}
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
- const urls = prepareUrls(protocol, HOST, port);
+ const urls = prepareUrls(protocol, HOST, port, servedPathPathname);
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
// Load proxy config
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index d40c87ebc81..e95182a8087 100644
--- a/packages/react-scripts/template/README.md
+++ b/packages/react-scripts/template/README.md
@@ -300,7 +300,7 @@ In the WebStorm menu `Run` select `Edit Configurations...`. Then click `+` and s
Start your app by running `npm start`, then press `^D` on macOS or `F9` on Windows and Linux or click the green debug icon to start debugging in WebStorm.
-The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine.
+The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine.
## Formatting Code Automatically
@@ -1989,7 +1989,7 @@ If you’re using [Apache HTTP Server](https://httpd.apache.org/), you need to c
RewriteRule ^ index.html [QSA,L]
```
-It will get copied to the `build` folder when you run `npm run build`.
+It will get copied to the `build` folder when you run `npm run build`.
If you’re using [Apache Tomcat](http://tomcat.apache.org/), you need to follow [this Stack Overflow answer](https://stackoverflow.com/a/41249464/4878474).
@@ -2021,6 +2021,13 @@ To override this, specify the `homepage` in your `package.json`, for example:
This will let Create React App correctly infer the root path to use in the generated HTML file.
+If `homepage` is specified, Create React App will open your browser at the path specified. From the example above, `npm start` would result in:
+
+```js
+http://localhost:3000/relativepath
+```
+This also means that in development the paths to the static files will be served out of the `relativepath` directory.
+
**Note**: If you are using `react-router@^4`, you can root ``s using the `basename` prop on any ``.
More information [here](https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string).