@atlaspack/reporter-dev-server 2.14.18-unified-f92fba5b6.0 → 2.14.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Server.js ADDED
@@ -0,0 +1,459 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.SOURCES_ENDPOINT = void 0;
7
+ exports.setHeaders = setHeaders;
8
+ function _assert() {
9
+ const data = _interopRequireDefault(require("assert"));
10
+ _assert = function () {
11
+ return data;
12
+ };
13
+ return data;
14
+ }
15
+ function _path() {
16
+ const data = _interopRequireDefault(require("path"));
17
+ _path = function () {
18
+ return data;
19
+ };
20
+ return data;
21
+ }
22
+ function _url() {
23
+ const data = _interopRequireWildcard(require("url"));
24
+ _url = function () {
25
+ return data;
26
+ };
27
+ return data;
28
+ }
29
+ function _utils() {
30
+ const data = require("@atlaspack/utils");
31
+ _utils = function () {
32
+ return data;
33
+ };
34
+ return data;
35
+ }
36
+ var _serverErrors = _interopRequireDefault(require("./serverErrors"));
37
+ function _fs() {
38
+ const data = _interopRequireDefault(require("fs"));
39
+ _fs = function () {
40
+ return data;
41
+ };
42
+ return data;
43
+ }
44
+ function _ejs() {
45
+ const data = _interopRequireDefault(require("ejs"));
46
+ _ejs = function () {
47
+ return data;
48
+ };
49
+ return data;
50
+ }
51
+ function _connect() {
52
+ const data = _interopRequireDefault(require("connect"));
53
+ _connect = function () {
54
+ return data;
55
+ };
56
+ return data;
57
+ }
58
+ function _serveHandler() {
59
+ const data = _interopRequireDefault(require("serve-handler"));
60
+ _serveHandler = function () {
61
+ return data;
62
+ };
63
+ return data;
64
+ }
65
+ function _httpProxyMiddleware() {
66
+ const data = require("http-proxy-middleware");
67
+ _httpProxyMiddleware = function () {
68
+ return data;
69
+ };
70
+ return data;
71
+ }
72
+ function _launchEditor() {
73
+ const data = _interopRequireDefault(require("launch-editor"));
74
+ _launchEditor = function () {
75
+ return data;
76
+ };
77
+ return data;
78
+ }
79
+ function _fresh() {
80
+ const data = _interopRequireDefault(require("fresh"));
81
+ _fresh = function () {
82
+ return data;
83
+ };
84
+ return data;
85
+ }
86
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
87
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
88
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
89
+ function setHeaders(res) {
90
+ res.setHeader('Access-Control-Allow-Origin', '*');
91
+ res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, PUT, PATCH, POST, DELETE');
92
+ res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Content-Type');
93
+ res.setHeader('Cache-Control', 'max-age=0, must-revalidate');
94
+ }
95
+ const SLASH_REGEX = /\//g;
96
+ const SOURCES_ENDPOINT = exports.SOURCES_ENDPOINT = '/__parcel_source_root';
97
+ const EDITOR_ENDPOINT = '/__parcel_launch_editor';
98
+ const TEMPLATE_404 = _fs().default.readFileSync(_path().default.join(__dirname, '..', 'templates/404.html'), 'utf8');
99
+ const TEMPLATE_500 = _fs().default.readFileSync(_path().default.join(__dirname, '..', 'templates/500.html'), 'utf8');
100
+ class Server {
101
+ constructor(options) {
102
+ this.options = options;
103
+ try {
104
+ this.rootPath = new (_url().URL)(options.publicUrl).pathname;
105
+ } catch (e) {
106
+ this.rootPath = options.publicUrl;
107
+ }
108
+ this.pending = true;
109
+ this.pendingRequests = [];
110
+ this.middleware = [];
111
+ this.bundleGraph = null;
112
+ this.requestBundle = null;
113
+ this.errors = null;
114
+ }
115
+ buildStart() {
116
+ this.pending = true;
117
+ }
118
+ buildSuccess(bundleGraph, requestBundle) {
119
+ this.bundleGraph = bundleGraph;
120
+ this.requestBundle = requestBundle;
121
+ this.errors = null;
122
+ this.pending = false;
123
+ if (this.pendingRequests.length > 0) {
124
+ let pendingRequests = this.pendingRequests;
125
+ this.pendingRequests = [];
126
+ for (let [req, res] of pendingRequests) {
127
+ this.respond(req, res);
128
+ }
129
+ }
130
+ }
131
+ async buildError(options, diagnostics) {
132
+ this.pending = false;
133
+ this.errors = await Promise.all(diagnostics.map(async d => {
134
+ let ansiDiagnostic = await (0, _utils().prettyDiagnostic)(d, options);
135
+ return {
136
+ message: (0, _utils().ansiHtml)(ansiDiagnostic.message),
137
+ stack: ansiDiagnostic.stack ? (0, _utils().ansiHtml)(ansiDiagnostic.stack) : null,
138
+ frames: ansiDiagnostic.frames.map(f => ({
139
+ location: f.location,
140
+ code: (0, _utils().ansiHtml)(f.code)
141
+ })),
142
+ hints: ansiDiagnostic.hints.map(hint => (0, _utils().ansiHtml)(hint)),
143
+ documentation: d.documentationURL ?? ''
144
+ };
145
+ }));
146
+ }
147
+ respond(req, res) {
148
+ if (this.middleware.some(handler => handler(req, res))) return;
149
+ let {
150
+ pathname,
151
+ search
152
+ } = _url().default.parse(req.originalUrl || req.url);
153
+ if (pathname == null) {
154
+ pathname = '/';
155
+ }
156
+ if (pathname.startsWith(EDITOR_ENDPOINT) && search) {
157
+ let query = new (_url().URLSearchParams)(search);
158
+ let file = query.get('file');
159
+ if (file) {
160
+ // File location might start with /__parcel_source_root if it came from a source map.
161
+ if (file.startsWith(SOURCES_ENDPOINT)) {
162
+ file = file.slice(SOURCES_ENDPOINT.length + 1);
163
+ }
164
+ (0, _launchEditor().default)(file);
165
+ }
166
+ res.end();
167
+ } else if (this.errors) {
168
+ return this.send500(req, res);
169
+ } else if (_path().default.extname(pathname) === '') {
170
+ // If the URL doesn't start with the public path, or the URL doesn't
171
+ // have a file extension, send the main HTML bundle.
172
+ return this.sendIndex(req, res);
173
+ } else if (pathname.startsWith(SOURCES_ENDPOINT)) {
174
+ req.url = pathname.slice(SOURCES_ENDPOINT.length);
175
+ return this.serve(this.options.inputFS, this.options.projectRoot, req, res, () => this.send404(req, res));
176
+ } else if (pathname.startsWith(this.rootPath)) {
177
+ // Otherwise, serve the file from the dist folder
178
+ req.url = this.rootPath === '/' ? pathname : pathname.slice(this.rootPath.length);
179
+ if (req.url[0] !== '/') {
180
+ req.url = '/' + req.url;
181
+ }
182
+ return this.serveBundle(req, res, () => this.sendIndex(req, res));
183
+ } else {
184
+ return this.send404(req, res);
185
+ }
186
+ }
187
+ sendIndex(req, res) {
188
+ if (this.bundleGraph) {
189
+ // If the main asset is an HTML file, serve it
190
+ let htmlBundleFilePaths = this.bundleGraph.getBundles().filter(bundle => _path().default.posix.extname(bundle.name) === '.html').map(bundle => {
191
+ return `/${(0, _utils().relativePath)(this.options.distDir, bundle.filePath, false)}`;
192
+ });
193
+ let indexFilePath = null;
194
+ let {
195
+ pathname: reqURL
196
+ } = _url().default.parse(req.originalUrl || req.url);
197
+ if (!reqURL) {
198
+ reqURL = '/';
199
+ }
200
+ if (htmlBundleFilePaths.length === 1) {
201
+ indexFilePath = htmlBundleFilePaths[0];
202
+ } else {
203
+ var _bestMatch;
204
+ let bestMatch = null;
205
+ for (let bundle of htmlBundleFilePaths) {
206
+ let bundleDir = _path().default.posix.dirname(bundle);
207
+ let bundleDirSubdir = bundleDir === '/' ? bundleDir : bundleDir + '/';
208
+ let withoutExtension = _path().default.posix.basename(bundle, _path().default.posix.extname(bundle));
209
+ let isIndex = withoutExtension === 'index';
210
+ let matchesIsIndex = null;
211
+ if (isIndex && (reqURL.startsWith(bundleDirSubdir) || reqURL === bundleDir)) {
212
+ // bundle is /bar/index.html and (/bar or something inside of /bar/** was requested was requested)
213
+ matchesIsIndex = true;
214
+ } else if (reqURL == _path().default.posix.join(bundleDir, withoutExtension)) {
215
+ // bundle is /bar/foo.html and /bar/foo was requested
216
+ matchesIsIndex = false;
217
+ }
218
+ if (matchesIsIndex != null) {
219
+ var _bundle$match;
220
+ let depth = ((_bundle$match = bundle.match(SLASH_REGEX)) === null || _bundle$match === void 0 ? void 0 : _bundle$match.length) ?? 0;
221
+ if (bestMatch == null ||
222
+ // This one is more specific (deeper)
223
+ bestMatch.depth < depth ||
224
+ // This one is just as deep, but the bundle name matches and not just index.html
225
+ bestMatch.depth === depth && bestMatch.isIndex) {
226
+ bestMatch = {
227
+ bundle,
228
+ depth,
229
+ isIndex: matchesIsIndex
230
+ };
231
+ }
232
+ }
233
+ }
234
+ indexFilePath = ((_bestMatch = bestMatch) === null || _bestMatch === void 0 ? void 0 : _bestMatch['bundle']) ?? htmlBundleFilePaths[0];
235
+ }
236
+ if (indexFilePath) {
237
+ req.url = indexFilePath;
238
+ this.serveBundle(req, res, () => this.send404(req, res));
239
+ } else {
240
+ this.send404(req, res);
241
+ }
242
+ } else {
243
+ this.send404(req, res);
244
+ }
245
+ }
246
+ async serveBundle(req, res, next) {
247
+ let bundleGraph = this.bundleGraph;
248
+ if (bundleGraph) {
249
+ let {
250
+ pathname
251
+ } = _url().default.parse(req.url);
252
+ if (!pathname) {
253
+ this.send500(req, res);
254
+ return;
255
+ }
256
+ let requestedPath = _path().default.normalize(pathname.slice(1));
257
+ let bundle = bundleGraph.getBundles().find(b => _path().default.relative(this.options.distDir, b.filePath) === requestedPath);
258
+ if (!bundle) {
259
+ this.serveDist(req, res, next);
260
+ return;
261
+ }
262
+ (0, _assert().default)(this.requestBundle != null);
263
+ try {
264
+ await this.requestBundle(bundle);
265
+ } catch (err) {
266
+ this.send500(req, res);
267
+ return;
268
+ }
269
+ this.serveDist(req, res, next);
270
+ } else {
271
+ this.send404(req, res);
272
+ }
273
+ }
274
+ serveDist(req, res, next) {
275
+ return this.serve(this.options.outputFS, this.options.distDir, req, res, next);
276
+ }
277
+ async serve(fs, root, req, res, next) {
278
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
279
+ // method not allowed
280
+ res.statusCode = 405;
281
+ res.setHeader('Allow', 'GET, HEAD');
282
+ res.setHeader('Content-Length', '0');
283
+ res.end();
284
+ return;
285
+ }
286
+ try {
287
+ var filePath = _url().default.parse(req.url).pathname || '';
288
+ filePath = decodeURIComponent(filePath);
289
+ } catch (err) {
290
+ return this.sendError(res, 400);
291
+ }
292
+ filePath = _path().default.normalize('.' + _path().default.sep + filePath);
293
+
294
+ // malicious path
295
+ if (filePath.includes(_path().default.sep + '..' + _path().default.sep)) {
296
+ return this.sendError(res, 403);
297
+ }
298
+
299
+ // join / normalize from the root dir
300
+ if (!_path().default.isAbsolute(filePath)) {
301
+ filePath = _path().default.normalize(_path().default.join(root, filePath));
302
+ }
303
+ try {
304
+ var stat = await fs.stat(filePath);
305
+ } catch (err) {
306
+ if (err.code === 'ENOENT') {
307
+ return next(req, res);
308
+ }
309
+ return this.sendError(res, 500);
310
+ }
311
+
312
+ // Fall back to next handler if not a file
313
+ if (!stat || !stat.isFile()) {
314
+ return next(req, res);
315
+ }
316
+ if ((0, _fresh().default)(req.headers, {
317
+ 'last-modified': stat.mtime.toUTCString()
318
+ })) {
319
+ res.statusCode = 304;
320
+ res.end();
321
+ return;
322
+ }
323
+ return (0, _serveHandler().default)(req, res, {
324
+ public: root,
325
+ cleanUrls: false
326
+ }, {
327
+ lstat: path => fs.stat(path),
328
+ realpath: path => fs.realpath(path),
329
+ createReadStream: (path, options) => fs.createReadStream(path, options),
330
+ readdir: path => fs.readdir(path)
331
+ });
332
+ }
333
+ sendError(res, statusCode) {
334
+ res.statusCode = statusCode;
335
+ res.end();
336
+ }
337
+ send404(req, res) {
338
+ res.statusCode = 404;
339
+ res.end(TEMPLATE_404);
340
+ }
341
+ send500(req, res) {
342
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
343
+ res.writeHead(500);
344
+ if (this.errors) {
345
+ return res.end(_ejs().default.render(TEMPLATE_500, {
346
+ errors: this.errors,
347
+ hmrOptions: this.options.hmrOptions
348
+ }));
349
+ }
350
+ }
351
+ logAccessIfVerbose(req) {
352
+ this.options.logger.verbose({
353
+ message: `Request: ${req.headers.host}${req.originalUrl || req.url}`
354
+ });
355
+ }
356
+
357
+ /**
358
+ * Load proxy table from package.json and apply them.
359
+ */
360
+ async applyProxyTable(app) {
361
+ // avoid skipping project root
362
+ const fileInRoot = _path().default.join(this.options.projectRoot, 'index');
363
+ const configFilePath = await (0, _utils().resolveConfig)(this.options.inputFS, fileInRoot, ['.proxyrc.cts', '.proxyrc.mts', '.proxyrc.ts', '.proxyrc.cjs', '.proxyrc.mjs', '.proxyrc.js', '.proxyrc', '.proxyrc.json'], this.options.projectRoot);
364
+ if (!configFilePath) {
365
+ return this;
366
+ }
367
+ const filename = _path().default.basename(configFilePath);
368
+ if (filename === '.proxyrc' || filename === '.proxyrc.json') {
369
+ let conf = await (0, _utils().readConfig)(this.options.inputFS, configFilePath);
370
+ if (!conf) {
371
+ return this;
372
+ }
373
+ let cfg = conf.config;
374
+ if (typeof cfg !== 'object') {
375
+ this.options.logger.warn({
376
+ message: "Proxy table in '.proxyrc' should be of object type. Skipping..."
377
+ });
378
+ return this;
379
+ }
380
+ for (const [context, options] of Object.entries(cfg)) {
381
+ // each key is interpreted as context, and value as middleware options
382
+ app.use((0, _httpProxyMiddleware().createProxyMiddleware)(context, options));
383
+ }
384
+ } else {
385
+ let cfg = await this.options.packageManager.require(configFilePath, fileInRoot);
386
+ if (
387
+ // $FlowFixMe
388
+ Object.prototype.toString.call(cfg) === '[object Module]') {
389
+ cfg = cfg.default;
390
+ }
391
+ if (typeof cfg !== 'function') {
392
+ this.options.logger.warn({
393
+ message: `Proxy configuration file '${filename}' should export a function. Skipping...`
394
+ });
395
+ return this;
396
+ }
397
+ cfg(app);
398
+ }
399
+ return this;
400
+ }
401
+ async start() {
402
+ const finalHandler = (req, res) => {
403
+ this.logAccessIfVerbose(req);
404
+
405
+ // Wait for the parcelInstance to finish bundling if needed
406
+ if (this.pending) {
407
+ this.pendingRequests.push([req, res]);
408
+ } else {
409
+ this.respond(req, res);
410
+ }
411
+ };
412
+ const app = (0, _connect().default)();
413
+ app.use((req, res, next) => {
414
+ setHeaders(res);
415
+ next();
416
+ });
417
+ app.use((req, res, next) => {
418
+ if (req.url === '/__parcel_healthcheck') {
419
+ res.statusCode = 200;
420
+ res.write(`${Date.now()}`);
421
+ res.end();
422
+ } else {
423
+ next();
424
+ }
425
+ });
426
+ await this.applyProxyTable(app);
427
+ app.use(finalHandler);
428
+ let {
429
+ server,
430
+ stop
431
+ } = await (0, _utils().createHTTPServer)({
432
+ cacheDir: this.options.cacheDir,
433
+ https: this.options.https,
434
+ inputFS: this.options.inputFS,
435
+ listener: app,
436
+ outputFS: this.options.outputFS,
437
+ host: this.options.host
438
+ });
439
+ this.stopServer = stop;
440
+ server.listen(this.options.port, this.options.host);
441
+ return new Promise((resolve, reject) => {
442
+ server.once('error', err => {
443
+ this.options.logger.error({
444
+ message: (0, _serverErrors.default)(err, this.options.port)
445
+ });
446
+ reject(err);
447
+ });
448
+ server.once('listening', () => {
449
+ resolve(server);
450
+ });
451
+ });
452
+ }
453
+ async stop() {
454
+ (0, _assert().default)(this.stopServer != null);
455
+ await this.stopServer();
456
+ this.stopServer = null;
457
+ }
458
+ }
459
+ exports.default = Server;