2014-08-04 00:01:39 +02:00
|
|
|
/**
|
|
|
|
* Module to inline styles while loading the existing stylesheets async
|
|
|
|
*
|
|
|
|
* @author Ben Zörb @bezoerb https://github.com/bezoerb
|
|
|
|
* @copyright Copyright (c) 2014 Ben Zörb
|
|
|
|
*
|
|
|
|
* Licensed under the MIT license.
|
|
|
|
* http://bezoerb.mit-license.org/
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
'use strict';
|
2014-10-19 03:12:33 +02:00
|
|
|
|
|
|
|
var fs = require('fs');
|
|
|
|
var path = require('path');
|
2014-12-08 23:02:01 +01:00
|
|
|
var UglifyJS = require("uglify-js");
|
2014-10-19 03:12:33 +02:00
|
|
|
var cave = require('cave');
|
2014-10-20 16:44:53 +02:00
|
|
|
var reaver = require('reaver');
|
2014-08-04 00:01:39 +02:00
|
|
|
var cheerio = require('cheerio');
|
2015-02-17 00:59:53 +01:00
|
|
|
var render = require('dom-serializer');
|
|
|
|
var parse = require('cheerio/lib/parse');
|
2014-08-04 00:01:39 +02:00
|
|
|
var CleanCSS = require('clean-css');
|
2014-11-25 17:21:43 +01:00
|
|
|
var slash = require('slash');
|
2014-11-25 23:15:48 +01:00
|
|
|
var normalizeNewline = require('normalize-newline');
|
2014-12-08 23:15:13 +01:00
|
|
|
// get loadCSS
|
|
|
|
var loadCSS = read(path.join(__dirname,'vendor','loadCSS.js'));
|
|
|
|
loadCSS = UglifyJS.minify(loadCSS, {fromString: true}).code;
|
2014-12-08 23:02:01 +01:00
|
|
|
|
2014-11-25 17:21:43 +01:00
|
|
|
/**
|
|
|
|
* Fixup slashes in file paths for windows
|
2014-12-08 23:15:13 +01:00
|
|
|
* @param {string} str
|
|
|
|
* @return {string}
|
2014-11-25 17:21:43 +01:00
|
|
|
*/
|
|
|
|
function normalizePath(str) {
|
|
|
|
return process.platform === 'win32' ? slash(str) : str;
|
|
|
|
}
|
2014-08-04 00:01:39 +02:00
|
|
|
|
2014-12-08 23:15:13 +01:00
|
|
|
/**
|
|
|
|
* Read file *
|
|
|
|
* @param {string} file
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2014-12-08 23:02:01 +01:00
|
|
|
function read (file) {
|
|
|
|
return fs.readFileSync(file, 'utf8');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-10-19 03:12:33 +02:00
|
|
|
module.exports = function(html, styles, options) {
|
2014-08-04 00:01:39 +02:00
|
|
|
|
|
|
|
var $ = cheerio.load(String(html));
|
|
|
|
var links = $('link[rel="stylesheet"]');
|
|
|
|
var noscript = $('<noscript>\n</noscript>');
|
2014-10-19 03:12:33 +02:00
|
|
|
var o = options || {};
|
2014-08-04 00:01:39 +02:00
|
|
|
|
|
|
|
// minify if minify option is set
|
2014-10-19 03:12:33 +02:00
|
|
|
if (o.minify) {
|
2015-02-17 00:59:53 +01:00
|
|
|
styles = new CleanCSS().minify(styles).styles;
|
2014-08-04 00:01:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// insert inline styles right before first <link rel="stylesheet" />
|
|
|
|
links.eq(0).before('<style type="text/css">\n' + styles + '\n</style>\n');
|
|
|
|
// insert noscript block right after stylesheets
|
|
|
|
links.eq(0).first().after(noscript);
|
|
|
|
|
|
|
|
var hrefs = links.map(function(idx, el) {
|
2014-10-20 16:44:53 +02:00
|
|
|
return $(this).attr('href');
|
2014-08-04 00:01:39 +02:00
|
|
|
}).toArray();
|
|
|
|
|
2014-10-19 03:12:33 +02:00
|
|
|
// extract styles from stylesheets if extract option is set
|
|
|
|
if (o.extract) {
|
|
|
|
if (!o.basePath) {
|
|
|
|
throw new Error('Option `basePath` is missing and required when using `extract`!');
|
|
|
|
}
|
2014-10-20 16:44:53 +02:00
|
|
|
hrefs = hrefs.map(function(href) {
|
2014-10-19 03:12:33 +02:00
|
|
|
var file = path.resolve(o.basePath, href);
|
2015-02-23 17:04:34 +01:00
|
|
|
if (!fs.existsSync(file) ) {
|
|
|
|
return href;
|
2014-10-19 03:12:33 +02:00
|
|
|
}
|
2014-11-25 23:15:48 +01:00
|
|
|
var diff = normalizeNewline(cave(file, { css: styles }));
|
2014-10-20 16:44:53 +02:00
|
|
|
fs.writeFileSync(reaver.rev(file, diff), diff);
|
2014-11-25 17:28:56 +01:00
|
|
|
return normalizePath(reaver.rev(href, diff));
|
2014-10-19 03:12:33 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-10-20 16:44:53 +02:00
|
|
|
// wrap links to stylesheets in noscript block so that they will evaluated when js is turned off
|
|
|
|
links.each(function (idx) {
|
|
|
|
var el = $(this);
|
|
|
|
el.attr('href', hrefs[idx]);
|
|
|
|
noscript.append(el);
|
|
|
|
noscript.append('\n');
|
|
|
|
});
|
|
|
|
|
2015-02-17 00:59:53 +01:00
|
|
|
// build js block to load blocking stylesheets and insert it right before
|
2014-12-08 21:33:40 +01:00
|
|
|
$(noscript).before('<script>\n' +
|
2014-12-08 23:02:01 +01:00
|
|
|
'(function(u){' +
|
|
|
|
loadCSS +
|
|
|
|
'for(var i in u){loadCSS(u[i]);}' +
|
|
|
|
'}([\'' + hrefs.join('\',\'') + '\']));\n' +
|
2014-08-04 00:01:39 +02:00
|
|
|
'</script>\n');
|
|
|
|
|
2015-02-17 00:59:53 +01:00
|
|
|
|
|
|
|
var dom = parse($.html());
|
|
|
|
var markup = render(dom);
|
|
|
|
|
|
|
|
return new Buffer(markup);
|
2014-08-04 00:01:39 +02:00
|
|
|
};
|