UNPKG

4.72 kBJavaScriptView Raw
1const http = require('http');
2const https = require('https');
3const serveStatic = require('serve-static');
4const getPort = require('get-port');
5const serverErrors = require('./utils/customErrors').serverErrors;
6const generateCertificate = require('./utils/generateCertificate');
7const getCertificate = require('./utils/getCertificate');
8const AnsiToHtml = require('ansi-to-html');
9const logger = require('@parcel/logger');
10const path = require('path');
11const url = require('url');
12
13const ansiToHtml = new AnsiToHtml({newline: true});
14
15serveStatic.mime.define({
16 'application/wasm': ['wasm']
17});
18
19function setHeaders(res) {
20 enableCors(res);
21}
22
23function enableCors(res) {
24 res.setHeader('Access-Control-Allow-Origin', '*');
25 res.setHeader(
26 'Access-Control-Allow-Methods',
27 'GET, HEAD, PUT, PATCH, POST, DELETE'
28 );
29 res.setHeader(
30 'Access-Control-Allow-Headers',
31 'Origin, X-Requested-With, Content-Type, Accept, Content-Type'
32 );
33}
34
35function middleware(bundler) {
36 const serve = serveStatic(bundler.options.outDir, {
37 index: false,
38 redirect: false,
39 setHeaders: setHeaders,
40 dotfiles: 'allow'
41 });
42
43 return function(req, res, next) {
44 logAccessIfVerbose();
45
46 // Wait for the bundler to finish bundling if needed
47 if (bundler.pending) {
48 bundler.once('bundled', respond);
49 } else {
50 respond();
51 }
52
53 function respond() {
54 let {pathname} = url.parse(req.url);
55 if (bundler.error) {
56 return send500(bundler.error);
57 } else if (
58 !pathname.startsWith(bundler.options.publicURL) ||
59 path.extname(pathname) === ''
60 ) {
61 // If the URL doesn't start with the public path, or the URL doesn't
62 // have a file extension, send the main HTML bundle.
63 return sendIndex();
64 } else {
65 // Otherwise, serve the file from the dist folder
66 req.url = pathname.slice(bundler.options.publicURL.length);
67 return serve(req, res, sendIndex);
68 }
69 }
70
71 function sendIndex() {
72 // If the main asset is an HTML file, serve it
73 if (bundler.mainBundle.type === 'html') {
74 req.url = `/${path.basename(bundler.mainBundle.name)}`;
75 serve(req, res, send404);
76 } else {
77 send404();
78 }
79 }
80
81 function send500(error) {
82 setHeaders(res);
83 res.setHeader('Content-Type', 'text/html; charset=utf-8');
84 res.writeHead(500);
85 let errorMesssge = '<h1>🚨 Build Error</h1>';
86 if (process.env.NODE_ENV === 'production') {
87 errorMesssge += '<p><b>Check the console for details.</b></p>';
88 } else {
89 const {message, stack} = logger.formatError(error, {color: true});
90 errorMesssge += `<p><b>${message}</b></p>`;
91 if (stack) {
92 errorMesssge += `<div style="background: black; padding: 1rem;">${ansiToHtml.toHtml(
93 stack
94 )}</div>`;
95 }
96 }
97 res.end(
98 [
99 `<!doctype html>`,
100 `<head><title>🚨 Build Error</title></head>`,
101 `<body style="font-family: monospace; white-space: pre;">${errorMesssge}</body>`
102 ].join('')
103 );
104 }
105
106 function send404() {
107 if (next) {
108 return next();
109 }
110 setHeaders(res);
111 res.writeHead(404);
112 res.end();
113 }
114
115 function logAccessIfVerbose() {
116 const protocol = req.connection.encrypted ? 'https' : 'http';
117 const fullUrl = `${protocol}://${req.headers.host}${req.url}`;
118
119 logger.verbose(`Request: ${fullUrl}`);
120 }
121 };
122}
123
124async function serve(bundler, port, host, useHTTPS = false) {
125 let handler = middleware(bundler);
126 let server;
127 if (!useHTTPS) {
128 server = http.createServer(handler);
129 } else if (typeof useHTTPS === 'boolean') {
130 server = https.createServer(generateCertificate(bundler.options), handler);
131 } else {
132 server = https.createServer(await getCertificate(useHTTPS), handler);
133 }
134
135 let freePort = await getPort({port});
136 server.listen(freePort, host);
137
138 return new Promise((resolve, reject) => {
139 server.on('error', err => {
140 console.log(err);
141 logger.error(new Error(serverErrors(err, server.address().port)));
142 reject(err);
143 });
144
145 server.once('listening', () => {
146 let addon =
147 server.address().port !== port
148 ? `- ${logger.chalk.yellow(
149 `configured port ${port} could not be used.`
150 )}`
151 : '';
152
153 logger.persistent(
154 `Server running at ${logger.chalk.cyan(
155 `${useHTTPS ? 'https' : 'http'}://${host || 'localhost'}:${
156 server.address().port
157 }`
158 )} ${addon}`
159 );
160
161 resolve(server);
162 });
163 });
164}
165
166exports.middleware = middleware;
167exports.serve = serve;