UNPKG

rollup-plugin-serve-proxy

Version:
218 lines (186 loc) 7.41 kB
import { readFile } from 'fs'; import https, { createServer } from 'https'; import http, { createServer as createServer$1 } from 'http'; import { resolve } from 'path'; import mime from 'mime'; import opener from 'opener'; var server; /** * Serve your rolled up bundle like webpack-dev-server * @param {ServeOptions|string|string[]} options */ function serve (options) { if ( options === void 0 ) options = { contentBase: '' }; if (Array.isArray(options) || typeof options === 'string') { options = { contentBase: options }; } options.contentBase = Array.isArray(options.contentBase) ? options.contentBase : [options.contentBase]; options.host = options.host || 'localhost'; options.port = options.port || 10001; options.headers = options.headers || {}; options.https = options.https || false; options.openPage = options.openPage || ''; options.proxy = options.proxy || {}; mime.default_type = 'text/plain'; // Use http or https as needed var httpS = options.https ? https : http; var proxies = Object.keys(options.proxy).map(function (proxy) { return ({ proxy: proxy, destination: options.proxy[proxy], test: new RegExp(("/" + proxy)) }); }); var requestListener = function (request, response) { // Remove querystring var urlPath = decodeURI(request.url.split('?')[0]); Object.keys(options.headers).forEach(function (key) { response.setHeader(key, options.headers[key]); }); // Find the appropriate proxy for the request if one exists var proxy = proxies.find(function (ref) { var test = ref.test; return request.url.match(test); }); // If a proxy exists, forward the request to the appropriate server if (proxy && proxy.destination) { var destination = proxy.destination; var newDestination = "" + destination + (request.url); var headers = request.headers; var method = request.method; // Get the request contents var body = ''; request.on('data', function (chunk) { body += chunk; }); // Forward the request request.on('end', function () { var proxyRequest = httpS .request(newDestination, { headers: headers, method: method }, function (proxyResponse) { var data = ''; proxyResponse.on('data', function (chunk) { data += chunk; }); proxyResponse.on('end', function () { Object.keys(proxyResponse.headers).forEach(function (key) { return response.setHeader(key, proxyResponse.headers[key]); }); foundProxy(response, proxyResponse.statusCode, data); }); }) .end(body, 'utf-8'); proxyRequest.on('error', function (err) { return console.error(("There was a problem with the request for " + (request.url) + ": " + err)); }); proxyRequest.end(); }); } else { readFileFromContentBase(options.contentBase, urlPath, function (error, content, filePath) { if (!error) { return found(response, filePath, content) } if (error.code !== 'ENOENT') { response.writeHead(500); response.end('500 Internal Server Error' + '\n\n' + filePath + '\n\n' + Object.values(error).join('\n') + '\n\n(rollup-plugin-serve)', 'utf-8'); return } if (options.historyApiFallback) { var fallbackPath = typeof options.historyApiFallback === 'string' ? options.historyApiFallback : '/index.html'; readFileFromContentBase(options.contentBase, fallbackPath, function (error, content, filePath) { if (error) { notFound(response, filePath); } else { found(response, filePath, content); } }); } else { notFound(response, filePath); } }); } }; // release previous server instance if rollup is reloading configuration in watch mode if (server) { server.close(); } // If HTTPS options are available, create an HTTPS server if (options.https) { server = createServer(options.https, requestListener).listen(options.port, options.host); } else { server = createServer$1(requestListener).listen(options.port, options.host); } closeServerOnTermination(server); var running = options.verbose === false; return { name: 'serve', generateBundle: function generateBundle () { if (!running) { running = true; // Log which url to visit var url = (options.https ? 'https' : 'http') + '://' + options.host + ':' + options.port; options.contentBase.forEach(function (base) { console.log(green(url) + ' -> ' + resolve(base)); }); // Open browser if (options.open) { opener(url + options.openPage); } } } } } function readFileFromContentBase (contentBase, urlPath, callback) { var filePath = resolve(contentBase[0] || '.', '.' + urlPath); // Load index.html in directories if (urlPath.endsWith('/')) { filePath = resolve(filePath, 'index.html'); } readFile(filePath, function (error, content) { if (error && contentBase.length > 1) { // Try to read from next contentBase readFileFromContentBase(contentBase.slice(1), urlPath, callback); } else { // We know enough callback(error, content, filePath); } }); } function notFound (response, filePath) { response.writeHead(404); response.end('404 Not Found' + '\n\n' + filePath + '\n\n(rollup-plugin-serve)', 'utf-8'); } function found (response, filePath, content) { response.writeHead(200, { 'Content-Type': mime.getType(filePath) }); response.end(content, 'utf-8'); } function foundProxy (response, status, content) { response.writeHead(status); response.end(content, 'utf-8'); } function green (text) { return '\u001b[1m\u001b[32m' + text + '\u001b[39m\u001b[22m' } function closeServerOnTermination (server) { var terminationSignals = ['SIGINT', 'SIGTERM']; terminationSignals.forEach(function (signal) { process.on(signal, function () { server.close(); process.exit(); }); }); } /** * @typedef {Object} ServeOptions * @property {boolean} [open=false] Launch in browser (default: `false`) * @property {string} [openPage=''] Page to navigate to when opening the browser. Will not do anything if `open` is `false`. Remember to start with a slash e.g. `'/different/page'` * @property {boolean} [verbose=true] Show server address in console (default: `true`) * @property {string|string[]} [contentBase=''] Folder(s) to serve files from * @property {string|boolean} [historyApiFallback] Path to fallback page. Set to `true` to return index.html (200) instead of error page (404) * @property {string} [host='localhost'] Server host (default: `'localhost'`) * @property {number} [port=10001] Server port (default: `10001`) * @property {ServeOptionsHttps} [https=false] By default server will be served over HTTP (https: `false`). It can optionally be served over HTTPS * @property {{[header:string]: string}} [headers] Set headers */ /** * @typedef {Object} ServeOptionsHttps * @property {string|Buffer|Buffer[]|Object[]} key * @property {string|Buffer|Array<string|Buffer>} cert * @property {string|Buffer|Array<string|Buffer>} ca * @see https.ServerOptions */ export default serve;