UNPKG

5.96 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5const fs_1 = __importDefault(require("fs"));
6const url_1 = __importDefault(require("url"));
7const path_1 = __importDefault(require("path"));
8const mime_types_1 = __importDefault(require("mime-types"));
9const handlebars_1 = __importDefault(require("handlebars"));
10const resolvePath = require('resolve-path');
11const ansiHTML = require('ansi-html');
12// Resets foreground and background colors to black
13// and white respectively
14ansiHTML.setColors({
15 reset: ['#000', '#fff'],
16});
17const errorTemplate = handlebars_1.default.compile(fs_1.default.readFileSync(path_1.default.resolve(__dirname, 'templates/error.html')).toString());
18const dirTemplate = handlebars_1.default.compile(fs_1.default.readFileSync(path_1.default.resolve(__dirname, 'templates/dir.html')).toString());
19// You must call watcher.start() before you call `getMiddleware`
20//
21// This middleware is for development use only. It hasn't been reviewed
22// carefully enough to run on a production server.
23//
24// Supported options:
25// autoIndex (default: true) - set to false to disable directory listings
26// liveReloadPath - LiveReload script URL for error pages
27function handleRequest(outputPath, request, response, next, options) {
28 const urlObj = url_1.default.parse(request.url);
29 let pathname = urlObj.pathname || '';
30 let filename, stat, lastModified, buffer;
31 try {
32 filename = decodeURIComponent(pathname);
33 if (!filename) {
34 response.writeHead(400);
35 response.end();
36 return;
37 }
38 filename = resolvePath(outputPath, filename.substr(1));
39 }
40 catch (err) {
41 response.writeHead(err.status || 500);
42 response.end();
43 return;
44 }
45 try {
46 stat = fs_1.default.statSync(filename);
47 }
48 catch (e) {
49 // not found
50 next();
51 return;
52 }
53 if (stat.isDirectory()) {
54 const indexFilename = path_1.default.join(filename, 'index.html');
55 const hasIndex = fs_1.default.existsSync(indexFilename);
56 if (!hasIndex && !options.autoIndex) {
57 next();
58 return;
59 }
60 if (pathname[pathname.length - 1] !== '/') {
61 urlObj.pathname += '/';
62 urlObj.host = request.headers['host'];
63 urlObj.protocol = request.socket.encrypted ? 'https' : 'http';
64 response.setHeader('Location', url_1.default.format(urlObj));
65 response.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
66 response.writeHead(301);
67 response.end();
68 return;
69 }
70 if (!hasIndex) {
71 // implied: options.autoIndex is true
72 const context = {
73 url: request.url,
74 files: fs_1.default
75 .readdirSync(filename)
76 .sort()
77 .map(child => {
78 const stat = fs_1.default.statSync(path_1.default.join(filename, child)), isDir = stat.isDirectory();
79 return {
80 href: child + (isDir ? '/' : ''),
81 type: isDir
82 ? 'dir'
83 : path_1.default
84 .extname(child)
85 .replace('.', '')
86 .toLowerCase(),
87 };
88 }),
89 liveReloadPath: options.liveReloadPath,
90 };
91 response.setHeader('Content-Type', 'text/html; charset=utf-8');
92 response.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
93 response.writeHead(200);
94 response.end(dirTemplate(context));
95 return;
96 }
97 // otherwise serve index.html
98 filename = indexFilename;
99 stat = fs_1.default.statSync(filename);
100 }
101 lastModified = stat.mtime.toUTCString();
102 response.setHeader('Last-Modified', lastModified);
103 // nginx style treat last-modified as a tag since browsers echo it back
104 if (request.headers['if-modified-since'] === lastModified) {
105 response.writeHead(304);
106 response.end();
107 return;
108 }
109 response.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
110 response.setHeader('Content-Length', stat.size);
111 response.setHeader('Content-Type', mime_types_1.default.contentType(path_1.default.extname(filename)));
112 // read file sync so we don't hold open the file creating a race with
113 // the builder (Windows does not allow us to delete while the file is open).
114 buffer = fs_1.default.readFileSync(filename);
115 response.writeHead(200);
116 response.end(buffer);
117}
118module.exports = function getMiddleware(watcher, options = {}) {
119 if (options.autoIndex == null)
120 options.autoIndex = true;
121 const outputPath = path_1.default.resolve(watcher.builder.outputPath);
122 return async function broccoliMiddleware(request, response, next) {
123 if (watcher.currentBuild == null) {
124 throw new Error('Waiting for initial build to start');
125 }
126 try {
127 await watcher.currentBuild;
128 handleRequest(outputPath, request, response, next, options);
129 }
130 catch (error) {
131 // All errors thrown from builder.build() are guaranteed to be
132 // Builder.BuildError instances.
133 const context = {
134 stack: ansiHTML(error.stack || ''),
135 liveReloadPath: options.liveReloadPath,
136 payload: error.broccoliPayload,
137 };
138 response.setHeader('Content-Type', 'text/html; charset=utf-8');
139 response.writeHead(500);
140 response.end(errorTemplate(context));
141 return error.stack;
142 }
143 };
144};
145//# sourceMappingURL=middleware.js.map
\No newline at end of file