Skip to content

Commit 91749ad

Browse files
authored
Merge pull request #81 from peterszerzo/proxy
Support proxy setting in elm-package.json
2 parents 55cff7d + a4f305b commit 91749ad

File tree

4 files changed

+107
-9
lines changed

4 files changed

+107
-9
lines changed

config/paths.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ module.exports = {
88
ownModules: path.resolve(__dirname, '../node_modules'),
99
scripts: path.resolve(__dirname, '../scripts'),
1010
elmMake: path.resolve(__dirname, '../node_modules/.bin/elm-make')
11-
};
11+
};

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"chalk": "1.1.3",
4444
"clean-webpack-plugin": "0.1.14",
4545
"cli-table": "0.3.1",
46+
"connect-history-api-fallback": "^1.3.0",
4647
"cross-spawn": "5.0.1",
4748
"css-loader": "0.26.1",
4849
"dotenv": "^2.0.0",
@@ -55,6 +56,7 @@
5556
"file-loader": "0.9.0",
5657
"fs-extra": "1.0.0",
5758
"html-webpack-plugin": "2.24.1",
59+
"http-proxy-middleware": "^0.17.3",
5860
"minimist": "1.2.0",
5961
"path-exists": "3.0.0",
6062
"postcss-loader": "1.2.1",

scripts/start.js

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
process.env.NODE_ENV = 'development';
2-
// Load environment variables from .env file.
3-
// Suppress warnings if this file is missing.
4-
require('dotenv').config({silent: true});
5-
1+
const fs = require('fs');
62
const pathExists = require('path-exists');
73
const chalk = require('chalk');
84
const webpack = require('webpack');
@@ -11,6 +7,12 @@ const config = require('../config/webpack.config.dev');
117
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
128
const clearConsole = require('react-dev-utils/clearConsole');
139
const openBrowser = require('react-dev-utils/openBrowser');
10+
const historyApiFallback = require('connect-history-api-fallback');
11+
const httpProxyMiddleware = require('http-proxy-middleware');
12+
13+
// Load environment variables from .env file.
14+
// Suppress warnings if this file is missing.
15+
require('dotenv').config({silent: true});
1416

1517
if (pathExists.sync('elm-package.json') === false) {
1618
console.log('Please, run the build script from project root directory');
@@ -54,14 +56,108 @@ compiler.plugin('done', function (stats) {
5456
}
5557
});
5658

59+
60+
// We need to provide a custom onError function for httpProxyMiddleware.
61+
// It allows us to log custom error messages on the console.
62+
function onProxyError(proxy) {
63+
return function(err, req, res){
64+
var host = req.headers && req.headers.host;
65+
console.log(
66+
chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) +
67+
' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.'
68+
);
69+
console.log(
70+
'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
71+
chalk.cyan(err.code) + ').'
72+
);
73+
console.log();
74+
75+
// And immediately send the proper error response to the client.
76+
// Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
77+
if (res.writeHead && !res.headersSent) {
78+
res.writeHead(500);
79+
}
80+
res.end('Proxy error: Could not proxy request ' + req.url + ' from ' +
81+
host + ' to ' + proxy + ' (' + err.code + ').'
82+
);
83+
};
84+
}
85+
86+
function addMiddleware(devServer) {
87+
// `proxy` lets you to specify a fallback server during development.
88+
// Every unrecognized request will be forwarded to it.
89+
var proxy = JSON.parse(fs.readFileSync('elm-package.json', 'utf-8')).proxy;
90+
devServer.use(historyApiFallback({
91+
// Paths with dots should still use the history fallback.
92+
// See https://github.com/facebookincubator/create-react-app/issues/387.
93+
disableDotRule: true,
94+
// For single page apps, we generally want to fallback to /index.html.
95+
// However we also want to respect `proxy` for API calls.
96+
// So if `proxy` is specified, we need to decide which fallback to use.
97+
// We use a heuristic: if request `accept`s text/html, we pick /index.html.
98+
// Modern browsers include text/html into `accept` header when navigating.
99+
// However API calls like `fetch()` won’t generally accept text/html.
100+
// If this heuristic doesn’t work well for you, don’t use `proxy`.
101+
htmlAcceptHeaders: proxy ?
102+
[ 'text/html' ] :
103+
[ 'text/html', '*/*' ]
104+
}));
105+
if (proxy) {
106+
if (typeof proxy !== 'string') {
107+
console.log(chalk.red('When specified, "proxy" in package.json must be a string.'));
108+
console.log(chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".'));
109+
console.log(chalk.red('Either remove "proxy" from package.json, or make it a string.'));
110+
process.exit(1);
111+
}
112+
113+
// Otherwise, if proxy is specified, we will let it handle any request.
114+
// There are a few exceptions which we won't send to the proxy:
115+
// - /index.html (served as HTML5 history API fallback)
116+
// - /*.hot-update.json (WebpackDevServer uses this too for hot reloading)
117+
// - /sockjs-node/* (WebpackDevServer uses this for hot reloading)
118+
// Tip: use https://jex.im/regulex/ to visualize the regex
119+
var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/;
120+
121+
// Pass the scope regex both to Express and to the middleware for proxying
122+
// of both HTTP and WebSockets to work without false positives.
123+
var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), {
124+
target: proxy,
125+
logLevel: 'silent',
126+
onProxyReq: function(proxyReq) {
127+
// Browers may send Origin headers even with same-origin
128+
// requests. To prevent CORS issues, we have to change
129+
// the Origin to match the target URL.
130+
if (proxyReq.getHeader('origin')) {
131+
proxyReq.setHeader('origin', proxy);
132+
}
133+
},
134+
onError: onProxyError(proxy),
135+
secure: false,
136+
changeOrigin: true,
137+
ws: true
138+
});
139+
devServer.use(mayProxy, hpm);
140+
141+
// Listen for the websocket 'upgrade' event and upgrade the connection.
142+
// If this is not done, httpProxyMiddleware will not try to upgrade until
143+
// an initial plain HTTP request is made.
144+
devServer.listeningApp.on('upgrade', hpm.upgrade);
145+
}
146+
147+
// Finally, by now we have certainly resolved the URL.
148+
// It may be /index.html, so let the dev server try serving it again.
149+
devServer.use(devServer.middleware);
150+
}
151+
57152
const devServer = new WebpackDevServer(compiler, {
58153
hot: true,
59154
inline: true,
60155
publicPath: '/',
61-
quiet: true,
62-
historyApiFallback: true,
156+
quiet: true
63157
});
64158

159+
addMiddleware(devServer);
160+
65161
// Launch WebpackDevServer.
66162
devServer.listen(port, function (err) {
67163
if (err) {

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,7 @@ http-errors@~1.5.0:
15841584
setprototypeof "1.0.2"
15851585
statuses ">= 1.3.1 < 2"
15861586

1587-
http-proxy-middleware@~0.17.1:
1587+
http-proxy-middleware@^0.17.3, http-proxy-middleware@~0.17.1:
15881588
version "0.17.3"
15891589
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.3.tgz#940382147149b856084f5534752d5b5a8168cd1d"
15901590
dependencies:

0 commit comments

Comments
 (0)