Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serve app under homepage #4158

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/react-dev-utils/WebpackDevServerUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 === '::';
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dev-utils/errorOverlayMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions packages/react-dev-utils/noopServiceWorkerMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"clearConsole.js",
"crossSpawn.js",
"errorOverlayMiddleware.js",
"serveAppMiddleware.js",
"eslintFormatter.js",
"FileSizeReporter.js",
"formatWebpackMessages.js",
Expand Down
21 changes: 21 additions & 0 deletions packages/react-dev-utils/serveAppMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* 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';

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();
}
};
};
14 changes: 10 additions & 4 deletions packages/react-scripts/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -22,14 +23,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);

// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
Expand Down Expand Up @@ -111,6 +116,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 =>
Expand Down
16 changes: 14 additions & 2 deletions packages/react-scripts/config/webpackDevServer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@

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 {
Expand Down Expand Up @@ -86,18 +90,26 @@ module.exports = function(proxy, allowedHost) {
// Paths with dots should still use the history fallback.
// See https://github.com/facebook/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/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
app.use(noopServiceWorkerMiddleware(servedPathPathname));
// serves the app up from the homepage if specified
app.use(serveAppMiddleware(servedPathPathname));
// serve up static assets
app.use(
`${config.output.publicPath.slice(0, -1)}/static`,
express.static(paths.appPublic)
);
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('Integration', () => {

const prefix =
process.env.NODE_ENV === 'development'
? ''
? '/static'
: 'http://www.example.org/spa';
expect(doc.getElementById('feature-public-url').textContent).to.equal(
`${prefix}.`
Expand Down
5 changes: 4 additions & 1 deletion packages/react-scripts/scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ if (process.env.SKIP_PREFLIGHT_CHECK !== 'true') {
}
// @remove-on-eject-end

const url = require('url');
const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
Expand Down Expand Up @@ -56,6 +57,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(
Expand Down Expand Up @@ -89,7 +92,7 @@ checkBrowsers(paths.appPath)
}
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,
Expand Down
7 changes: 7 additions & 0 deletions packages/react-scripts/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2192,6 +2192,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 `<Link>`s using the `basename` prop on any `<Router>`.<br>
More information [here](https://reacttraining.com/react-router/web/api/BrowserRouter/basename-string).<br>
<br>
Expand Down