@dillonkearns/elm-graphql
Version:
<img src="https://cdn.jsdelivr.net/gh/martimatix/logo-graphqelm/logo.svg" alt="dillonearns/elm-graphql logo" width="40%" align="right">
191 lines (148 loc) • 5.14 kB
JavaScript
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _next(value) { step("next", value); } function _throw(err) { step("throw", err); } _next(); }); }; }
/**
* Module dependencies.
*/
const debug = require('debug')('koa-send');
const resolvePath = require('resolve-path');
const createError = require('http-errors');
const assert = require('assert');
const fs = require('mz/fs');
const {
normalize,
basename,
extname,
resolve,
parse,
sep
} = require('path');
/**
* Expose `send()`.
*/
module.exports = send;
/**
* Send file at `path` with the
* given `options` to the koa `ctx`.
*
* @param {Context} ctx
* @param {String} path
* @param {Object} [opts]
* @return {Function}
* @api public
*/
function send(_x, _x2) {
return _send.apply(this, arguments);
}
/**
* Check if it's hidden.
*/
function _send() {
_send = _asyncToGenerator(function* (ctx, path, opts = {}) {
assert(ctx, 'koa context required');
assert(path, 'pathname required'); // options
debug('send "%s" %j', path, opts);
const root = opts.root ? normalize(resolve(opts.root)) : '';
const trailingSlash = path[path.length - 1] === '/';
path = path.substr(parse(path).root.length);
const index = opts.index;
const maxage = opts.maxage || opts.maxAge || 0;
const immutable = opts.immutable || false;
const hidden = opts.hidden || false;
const format = opts.format !== false;
const extensions = Array.isArray(opts.extensions) ? opts.extensions : false;
const brotli = opts.brotli !== false;
const gzip = opts.gzip !== false;
const setHeaders = opts.setHeaders;
if (setHeaders && typeof setHeaders !== 'function') {
throw new TypeError('option setHeaders must be function');
} // normalize path
path = decode(path);
if (path === -1) return ctx.throw(400, 'failed to decode'); // index file support
if (index && trailingSlash) path += index;
path = resolvePath(root, path); // hidden file support, ignore
if (!hidden && isHidden(root, path)) return;
let encodingExt = ''; // serve brotli file when possible otherwise gzipped file when possible
if (ctx.acceptsEncodings('br', 'identity') === 'br' && brotli && (yield fs.exists(path + '.br'))) {
path = path + '.br';
ctx.set('Content-Encoding', 'br');
ctx.res.removeHeader('Content-Length');
encodingExt = '.br';
} else if (ctx.acceptsEncodings('gzip', 'identity') === 'gzip' && gzip && (yield fs.exists(path + '.gz'))) {
path = path + '.gz';
ctx.set('Content-Encoding', 'gzip');
ctx.res.removeHeader('Content-Length');
encodingExt = '.gz';
}
if (extensions && !/\.[^/]*$/.exec(path)) {
const list = [].concat(extensions);
for (let i = 0; i < list.length; i++) {
let ext = list[i];
if (typeof ext !== 'string') {
throw new TypeError('option extensions must be array of strings or false');
}
if (!/^\./.exec(ext)) ext = '.' + ext;
if (yield fs.exists(path + ext)) {
path = path + ext;
break;
}
}
} // stat
let stats;
try {
stats = yield fs.stat(path); // Format the path to serve static file servers
// and not require a trailing slash for directories,
// so that you can do both `/directory` and `/directory/`
if (stats.isDirectory()) {
if (format && index) {
path += '/' + index;
stats = yield fs.stat(path);
} else {
return;
}
}
} catch (err) {
const notfound = ['ENOENT', 'ENAMETOOLONG', 'ENOTDIR'];
if (notfound.includes(err.code)) {
throw createError(404, err);
}
err.status = 500;
throw err;
}
if (setHeaders) setHeaders(ctx.res, path, stats); // stream
ctx.set('Content-Length', stats.size);
if (!ctx.response.get('Last-Modified')) ctx.set('Last-Modified', stats.mtime.toUTCString());
if (!ctx.response.get('Cache-Control')) {
const directives = ['max-age=' + (maxage / 1000 | 0)];
if (immutable) {
directives.push('immutable');
}
ctx.set('Cache-Control', directives.join(','));
}
ctx.type = type(path, encodingExt);
ctx.body = fs.createReadStream(path);
return path;
});
return _send.apply(this, arguments);
}
function isHidden(root, path) {
path = path.substr(root.length).split(sep);
for (let i = 0; i < path.length; i++) {
if (path[i][0] === '.') return true;
}
return false;
}
/**
* File type.
*/
function type(file, ext) {
return ext !== '' ? extname(basename(file, ext)) : extname(file);
}
/**
* Decode `path`.
*/
function decode(path) {
try {
return decodeURIComponent(path);
} catch (err) {
return -1;
}
}