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).