Last active
December 4, 2019 12:40
-
-
Save mrcoles/ff979c09719a5e827eb2d122aa2c6332 to your computer and use it in GitHub Desktop.
A simple proxy script for controlling URL rewriting when running Parcel with multiple entry points (including https support)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const chalk = require('chalk'); | |
const fs = require('fs'); | |
const http = require('http'); | |
const https = require('https'); | |
const yargs = require('yargs'); | |
// Configuration | |
const DEFAULT_PARCEL_PORT = 4321; | |
const DEFEAULT_PROXY_PORT = 1234; | |
const DEFAULT_FILE = '.localproxy.json'; | |
const DEFAULT_EXTS = [ | |
'html', | |
'css', | |
'js', | |
'map', | |
'png', | |
'jpg', | |
'svg', | |
'webp', | |
'mp4', | |
'webm', | |
'webmanifest', | |
'json', | |
'yml', | |
'pdf' | |
]; | |
// re-use the parcel self-signed certs if they exist | |
const CERTS = { | |
key: '.cache/private.pem', | |
cert: '.cache/primary.crt' | |
}; | |
// Functions | |
const startProxy = async (cfg, parcelPort, proxyPort, isHttps, verbose) => { | |
if (isHttps) { | |
// HACK - allow self-signed certs | |
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; | |
} | |
if (verbose) { | |
const proxyUrl = chalk.magenta( | |
`${isHttps ? 'https' : 'http'}://localhost:${proxyPort}` | |
); | |
console.log( | |
chalk.bold(`\n** Starting parcel local proxy on ${proxyUrl} **\n`) | |
); | |
} | |
const mainPath = cfg.default || '/index.html'; | |
const paths = cfg.paths || []; | |
const defaultExts = cfg.resetExts === true ? [] : DEFAULT_EXTS; | |
const cfgExts = cfg.exts || []; | |
const extsSet = new Set([...defaultExts, ...cfgExts]); | |
const module = isHttps ? https : http; | |
if (isHttps) { | |
const options = await loadCerts(verbose); | |
module.createServer(options, onRequest).listen(proxyPort); | |
} else { | |
module.createServer(onRequest).listen(proxyPort); | |
} | |
function onRequest(client_req, client_res) { | |
const baseUrl = client_req.url.split('?')[0]; | |
const extSp = baseUrl | |
.split('/') | |
.pop() | |
.split('.'); | |
const ext = extSp.length > 1 ? extSp[extSp.length - 1] : ''; | |
let newPath = client_req.url; | |
if (paths[baseUrl]) { | |
newPath = paths[baseUrl]; | |
} else if (!ext || !extsSet.has(ext)) { | |
newPath = mainPath; | |
} | |
if (verbose >= 2) { | |
const extra = newPath !== client_req.url ? ` -> ${newPath}` : ''; | |
console.log(`serve: ${client_req.url}${extra}`); | |
} | |
if (verbose >= 3) { | |
console.log(`headers: ${JSON.stringify(client_req.headers, null, 2)}`); | |
} | |
const options = { | |
hostname: 'localhost', | |
port: parcelPort, | |
path: newPath, | |
method: client_req.method, | |
headers: client_req.headers | |
}; | |
const proxy = module.request(options, function(res) { | |
client_res.writeHead(res.statusCode, res.headers); | |
res.pipe( | |
client_res, | |
{ end: true } | |
); | |
}); | |
client_req.pipe( | |
proxy, | |
{ end: true } | |
); | |
} | |
}; | |
const loadCerts = async verbose => { | |
const results = {}; | |
while (true) { | |
let isOk = true; | |
for (let [key, filepath] of Object.entries(CERTS)) { | |
if (results[key] === undefined) { | |
try { | |
const contents = await readFile(filepath); | |
results[key] = contents; | |
} catch (err) { | |
if (verbose) { | |
console.log(`Unable to load ${filepath}: ${err.message}`); | |
} | |
await sleep(1000); | |
isOk = false; | |
break; | |
} | |
} | |
} | |
if (isOk) { | |
break; | |
} | |
} | |
return results; | |
}; | |
// Helpers | |
const sleep = delay => | |
new Promise(resolve => { | |
setTimeout(() => resolve(), delay); | |
}); | |
const readFile = path => | |
new Promise((resolve, reject) => { | |
fs.readFile(path, 'utf8', (err, contents) => { | |
return err ? reject(err) : resolve(contents); | |
}); | |
}); | |
const readFileSync = path => fs.readFileSync(path, 'utf8'); | |
// Main | |
const main = async () => { | |
return yargs | |
.usage('Usage: $0 <command> [options]') | |
.command( | |
'$0 <parcel-port> [proxy-port]', | |
'Start proxy server', | |
yargs => | |
yargs | |
.positional('parcel-port', { | |
type: 'string', | |
demand: true, | |
help: 'the port that parcel is running on' | |
}) | |
.positional('proxy-port', { | |
type: 'string', | |
default: DEFEAULT_PROXY_PORT, | |
help: 'the port that this proxy is running on' | |
}) | |
.count('verbose') | |
.alias('v', 'verbose') | |
.option('https', { type: 'boolean', default: false }) | |
.option('file', { | |
alias: 'f', | |
help: 'specify an alternate config file path', | |
default: DEFAULT_FILE | |
}), | |
async argv => { | |
console.log('FILE?', argv.file); | |
const cfgContents = readFileSync(argv.file); | |
const cfg = JSON.parse(cfgContents); | |
return startProxy( | |
cfg, | |
argv.parcelPort, | |
argv.proxyPort, | |
argv.https, | |
argv.verbose | |
); | |
} | |
) | |
.demandCommand(1, 1) | |
.alias('h', 'help') | |
.help().argv; | |
}; | |
// | |
if (require.main === module) { | |
main().catch(err => { | |
console.error(err); | |
process.exit(1); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What is this?
This script creates a proxy server that maps URLs that you request to the ones that Parcel server expects. This is intended to help you out when running parcel with multiple entry files, e.g., normally:
However, with this proxy, you can—among other things—specify that "/folder-1/" should proxy to "/folder-1/index.html".
Also, since I’m using parcel with the
--https
flag, I added support for--https
to this proxy too.Setup
Copy
parcel-local-proxy.js
into your projectInstall dependencies:
Update your package.json scripts:
Make sure the hardcoded port for parcel and proxy both match. Optionally, enable https on both. For more run
node parcel-local-proxy.js --help
Add a
.localproxy.json
file to the root directory of your project. The structure is:Run
Now you can just do:
And the local dev server will operate how you expect!