index.js 3.29 KB
"use strict";

var etag  = require("etag");
var fresh = require("fresh");
var fs    = require("fs");
var path  = require("path");
var zlib  = require("zlib");

var minifiedScript   = path.join(__dirname, "/dist/index.min.js");
var unminifiedScript = path.join(__dirname, "/dist/index.js");

/**
 * Does the current request support compressed encoding?
 * @param {Object} req
 * @returns {boolean}
 */
function supportsGzip (req) {
    var accept = req.headers['accept-encoding'];
    return accept && accept.indexOf('gzip') > -1;
}

/**
 * Set headers on the response
 * @param {Object} res
 * @param {String} body
 */
function setHeaders(res, body) {

    res.setHeader("Cache-Control", "public, max-age=0");
    res.setHeader("Content-Type", "text/javascript");
    res.setHeader("ETag", etag(body));
}

/**
 * @param {Object} options
 * @param {String} connector
 * @returns {String}
 */
function getScriptBody(options, connector) {

    var script = minifiedScript;

    if (options && !options.minify) {
        script = unminifiedScript;
    }

    return connector + fs.readFileSync(script);
}

/**
 * @param {Object} req
 * @returns {String}
 */
function isConditionalGet(req) {
    return req.headers["if-none-match"] || req.headers["if-modified-since"];
}

/**
 * Return a not-modified response
 * @param {Object} res
 */
function notModified(res) {
    res.removeHeader("Content-Type");
    res.statusCode = 304;
    res.end();
}

/**
 * Public method for returning either a middleware fn
 * or the content as a string
 * @param {Object} options
 * @param {String} connector - content to be prepended
 * @param {String} type - either `file` or `middleware`
 * @returns {*}
 */
function init(options, connector, type) {

    var gzipCached;

    /**
     * Combine string to create the final version
     * @type {String}
     */
    var requestBody = getScriptBody(options, connector);

    /**
     * If the user asked for a file, simply return the string.
     */
    if (type && type === "file") {
        return requestBody;
    }

    /**
     * Otherwise return a function to be used a middleware
     */
    return function (req, res) {

        /**
         * default to using the uncompressed string
         * @type {String}
         */
        var output = requestBody;

        /**
         * Set the appropriate headers for caching
         */
        setHeaders(res, output);

        if (isConditionalGet(req) && fresh(req.headers, res._headers)) {
            return notModified(res);
        }

        /**
         * If gzip is supported, compress the string once
         * and save for future requests
         */
        if (supportsGzip(req)) {

            res.setHeader("Content-Encoding", "gzip");

            if (!gzipCached) {
                var buf = new Buffer(output, "utf-8");
                zlib.gzip(buf, function (_, result) {
                    gzipCached = result;
                    res.end(result);
                });
            } else {
                res.end(gzipCached);
            }

        } else {
            res.end(output);
        }
    };
}

module.exports.middleware = init;
module.exports.plugin = init;
module.exports.minified = function () {
    return fs.readFileSync(minifiedScript, 'utf8');
};
module.exports.unminified = function () {
    return fs.readFileSync(unminifiedScript, 'utf8');
}