From 9707ed927e9442a7a56b2867379a499e9b04496d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B4=E6=BE=9C?= Date: Wed, 17 Jul 2019 11:01:05 +0800 Subject: [PATCH] feat: proxy for ssr dev server --- packages/rax-scripts/package.json | 2 +- packages/rax-scripts/src/config/index.js | 9 +- packages/rax-scripts/src/start.js | 22 +++- .../src/utils/createWebpackCompiler.js | 5 +- packages/rax-ssr-dev-server/README.md | 16 ++- packages/rax-ssr-dev-server/index.js | 124 +++++++++++++++++- packages/rax-ssr-dev-server/package.json | 3 +- 7 files changed, 165 insertions(+), 16 deletions(-) diff --git a/packages/rax-scripts/package.json b/packages/rax-scripts/package.json index 454d5f13f4..88185796fe 100644 --- a/packages/rax-scripts/package.json +++ b/packages/rax-scripts/package.json @@ -1,6 +1,6 @@ { "name": "rax-scripts", - "version": "1.3.9", + "version": "1.3.10", "license": "BSD-3-Clause", "description": "Project scripts for Rax.", "bin": { diff --git a/packages/rax-scripts/src/config/index.js b/packages/rax-scripts/src/config/index.js index bc1ec412c6..f69ad79957 100644 --- a/packages/rax-scripts/src/config/index.js +++ b/packages/rax-scripts/src/config/index.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const pathConfig = require('./path.config'); const appConfig = require('./app.config'); +const rewireWebpackConfig = require('../utils/rewireWebpackConfig'); const webpackConfigMap = { webapp: { @@ -15,7 +16,7 @@ const webpackConfigMap = { }; exports.getWebpackConfig = (type, env = 'prod') => { - let config = []; + let config; if (type === 'webapp') { config = [ require(`${webpackConfigMap.webapp.client}.${env}`) @@ -28,11 +29,11 @@ exports.getWebpackConfig = (type, env = 'prod') => { config.push(require(`${webpackConfigMap.webapp.serverless}.${env}`)); } } - - return config; + } else { + config = require(`${webpackConfigMap[type]}.${env}`); } - return require(`${webpackConfigMap[type]}.${env}`); + return rewireWebpackConfig(config); }; exports.getEntries = () => { diff --git a/packages/rax-scripts/src/start.js b/packages/rax-scripts/src/start.js index 68eb6fe71a..c1fa1cfaf2 100644 --- a/packages/rax-scripts/src/start.js +++ b/packages/rax-scripts/src/start.js @@ -47,12 +47,28 @@ module.exports = function start(type = 'webapp') { const webpackConfig = getWebpackConfig(type, 'dev'); const compiler = createWebpackCompiler(webpackConfig); - let devServer; + const devServerConfig = webpackDevServerConfig; + if (appConfig.ssr) { const ssrDevServerConfig = getDevServerConfig(); - devServer = new SSRDevServer(compiler, ssrDevServerConfig); + Object.assign(devServerConfig, ssrDevServerConfig); + } + + // rewire webpack dev config + if (Array.isArray(webpackConfig)) { + const customConfig = webpackConfig.find(config => { + return config.devServer; + }); + customConfig && Object.assign(devServerConfig, customConfig.devServer); + } else if (webpackConfig.devServer) { + Object.assign(devServerConfig, webpackConfig.devServer); + } + + let devServer; + if (appConfig.ssr) { + devServer = new SSRDevServer(compiler, devServerConfig); } else { - devServer = new WebpackDevServer(compiler, webpackDevServerConfig); + devServer = new WebpackDevServer(compiler, devServerConfig); } // Launch WebpackDevServer. diff --git a/packages/rax-scripts/src/utils/createWebpackCompiler.js b/packages/rax-scripts/src/utils/createWebpackCompiler.js index 6b4b0ba6f8..a8dc766ed1 100644 --- a/packages/rax-scripts/src/utils/createWebpackCompiler.js +++ b/packages/rax-scripts/src/utils/createWebpackCompiler.js @@ -2,7 +2,6 @@ /* eslint no-console: 0 */ const colors = require('chalk'); -const rewireWebpackConfig = require('./rewireWebpackConfig'); const webpack = require('webpack'); /** @@ -16,10 +15,8 @@ const webpack = require('webpack'); module.exports = (webpackConfig) => { let compiler; - const config = rewireWebpackConfig(webpackConfig); - try { - compiler = webpack(config); + compiler = webpack(webpackConfig); } catch (err) { console.error(colors.red('[ERR]: Failed to compile.')); console.log(''); diff --git a/packages/rax-ssr-dev-server/README.md b/packages/rax-ssr-dev-server/README.md index 86314f7e9a..e228f654df 100644 --- a/packages/rax-ssr-dev-server/README.md +++ b/packages/rax-ssr-dev-server/README.md @@ -76,4 +76,18 @@ assets info for pages absolute path for assets manifest file -If assets manifest is update with compilation, you can save it to a temp file and config this option. SSR will read the assets manifest before each render. \ No newline at end of file +If assets manifest is update with compilation, you can save it to a temp file and config this option. SSR will read the assets manifest before each render. + +### proxy + +Proxying some URLs can be useful when you have a separate API backend development server and you want to send API requests on the same domain. + +Config for proxy is same as [webpack-dev-server](https://webpack.js.org/configuration/dev-server/#devserverproxy). + +```json +{ + "proxy": { + "/api": "http://localhost:3000" + } +} +``` \ No newline at end of file diff --git a/packages/rax-ssr-dev-server/index.js b/packages/rax-ssr-dev-server/index.js index e5c8173aa5..9e38bda7a8 100644 --- a/packages/rax-ssr-dev-server/index.js +++ b/packages/rax-ssr-dev-server/index.js @@ -3,6 +3,7 @@ const express = require('express'); const RaxServer = require('rax-server'); const devMiddleware = require('webpack-dev-middleware'); const hotMiddleware = require('webpack-hot-middleware'); +const httpProxyMiddleware = require('http-proxy-middleware'); class DevServer { constructor(compiler, options) { @@ -12,6 +13,7 @@ class DevServer { setupApp(compiler) { const app = express(); + this.app = app; // eslint-disable-next-line new-cap const router = express.Router(); @@ -52,9 +54,11 @@ class DevServer { hotMiddleware(compiler) ); - app.use(router); + if (this.options.proxy) { + this.setupProxyFeature(); + } - this.app = app; + app.use(router); } close() { } @@ -110,6 +114,122 @@ class DevServer { const fs = res.locals.fs; return fs.readFileSync(filePath, 'utf8'); } + + // https://github.com/webpack/webpack-dev-server/blob/master/lib/Server.js + setupProxyFeature() { + /** + * Assume a proxy configuration specified as: + * proxy: { + * 'context': { options } + * } + * OR + * proxy: { + * 'context': 'target' + * } + */ + if (!Array.isArray(this.options.proxy)) { + if (Object.prototype.hasOwnProperty.call(this.options.proxy, 'target')) { + this.options.proxy = [this.options.proxy]; + } else { + this.options.proxy = Object.keys(this.options.proxy).map((context) => { + let proxyOptions; + // For backwards compatibility reasons. + const correctedContext = context + .replace(/^\*$/, '**') + .replace(/\/\*$/, ''); + + if (typeof this.options.proxy[context] === 'string') { + proxyOptions = { + context: correctedContext, + target: this.options.proxy[context], + }; + } else { + proxyOptions = Object.assign({}, this.options.proxy[context]); + proxyOptions.context = correctedContext; + } + + proxyOptions.logLevel = proxyOptions.logLevel || 'warn'; + + return proxyOptions; + }); + } + } + + const getProxyMiddleware = (proxyConfig) => { + const context = proxyConfig.context || proxyConfig.path; + + // It is possible to use the `bypass` method without a `target`. + // However, the proxy middleware has no use in this case, and will fail to instantiate. + if (proxyConfig.target) { + return httpProxyMiddleware(context, proxyConfig); + } + }; + /** + * Assume a proxy configuration specified as: + * proxy: [ + * { + * context: ..., + * ...options... + * }, + * // or: + * function() { + * return { + * context: ..., + * ...options... + * }; + * } + * ] + */ + this.options.proxy.forEach((proxyConfigOrCallback) => { + let proxyConfig; + let proxyMiddleware; + + if (typeof proxyConfigOrCallback === 'function') { + proxyConfig = proxyConfigOrCallback(); + } else { + proxyConfig = proxyConfigOrCallback; + } + + proxyMiddleware = getProxyMiddleware(proxyConfig); + + if (proxyConfig.ws) { + this.websocketProxies.push(proxyMiddleware); + } + + this.app.use((req, res, next) => { + if (typeof proxyConfigOrCallback === 'function') { + const newProxyConfig = proxyConfigOrCallback(); + + if (newProxyConfig !== proxyConfig) { + proxyConfig = newProxyConfig; + proxyMiddleware = getProxyMiddleware(proxyConfig); + } + } + + // - Check if we have a bypass function defined + // - In case the bypass function is defined we'll retrieve the + // bypassUrl from it otherwise byPassUrl would be null + const isByPassFuncDefined = typeof proxyConfig.bypass === 'function'; + const bypassUrl = isByPassFuncDefined + ? proxyConfig.bypass(req, res, proxyConfig) + : null; + + if (typeof bypassUrl === 'boolean') { + // skip the proxy + req.url = null; + next(); + } else if (typeof bypassUrl === 'string') { + // byPass to that url + req.url = bypassUrl; + next(); + } else if (proxyMiddleware) { + return proxyMiddleware(req, res, next); + } else { + next(); + } + }); + }); + } } function interopDefault(mod) { diff --git a/packages/rax-ssr-dev-server/package.json b/packages/rax-ssr-dev-server/package.json index 5c1be42331..c66d143f2a 100644 --- a/packages/rax-ssr-dev-server/package.json +++ b/packages/rax-ssr-dev-server/package.json @@ -1,6 +1,6 @@ { "name": "rax-ssr-dev-server", - "version": "1.0.0", + "version": "1.0.1", "description": "Rax SSR dev server.", "license": "BSD-3-Clause", "main": "index.js", @@ -17,6 +17,7 @@ }, "dependencies": { "express": "^4.17.1", + "http-proxy-middleware": "^0.19.1", "rax-server": "^1.0.0", "webpack-dev-middleware": "^3.7.0", "webpack-hot-middleware": "^2.25.0"