From 7484531212e0a3623ff27c7875215ae55e2d2312 Mon Sep 17 00:00:00 2001 From: Nicolas Bevacqua Date: Sat, 18 Oct 2014 22:12:33 -0300 Subject: [PATCH 1/4] implement extract options and test it out --- .editorconfig | 2 +- index.js | 24 ++- package.json | 1 + test/fixtures/cartoon-expected.css | 15 ++ test/fixtures/cartoon-src.css | 306 +++++++++++++++++++++++++++++ test/fixtures/cartoon.css | 15 ++ test/fixtures/cartoon.html | 88 +++++++++ test/index.js | 55 ++++-- 8 files changed, 484 insertions(+), 22 deletions(-) create mode 100644 test/fixtures/cartoon-expected.css create mode 100644 test/fixtures/cartoon-src.css create mode 100644 test/fixtures/cartoon.css create mode 100644 test/fixtures/cartoon.html diff --git a/.editorconfig b/.editorconfig index 6a804c2..2c6f376 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true diff --git a/index.js b/index.js index 0de3fa2..787c705 100644 --- a/index.js +++ b/index.js @@ -9,17 +9,22 @@ * All rights reserved. */ 'use strict'; + +var fs = require('fs'); +var path = require('path'); +var cave = require('cave'); var cheerio = require('cheerio'); var CleanCSS = require('clean-css'); -module.exports = function(html, styles, minify) { +module.exports = function(html, styles, options) { var $ = cheerio.load(String(html)); var links = $('link[rel="stylesheet"]'); var noscript = $(''); + var o = options || {}; // minify if minify option is set - if (minify) { + if (o.minify) { styles = new CleanCSS().minify(styles); } @@ -36,6 +41,21 @@ module.exports = function(html, styles, minify) { return el.attr('href'); }).toArray(); + // 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`!'); + } + hrefs.forEach(function(href) { + var file = path.resolve(o.basePath, href); + if (!fs.existsSync(file)) { + return; + } + var diff = cave(file, { css: styles }); + fs.writeFileSync(file, diff); + }); + } + // build js block to load blocking stylesheets $('body').append(' + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/index.js b/test/index.js index 740389c..b8a3180 100644 --- a/test/index.js +++ b/test/index.js @@ -1,38 +1,55 @@ -var expect = require('chai').expect, - fs = require('fs'), - inlineCritical = require('..'); +var expect = require('chai').expect; +var fs = require('fs'); +var inlineCritical = require('..'); -/** - * Strip whitespaces, tabs and newlines and replace with one space. - * Usefull when comparing string contents. - * @param string - */ -function stripWhitespace(string) { - return string.replace(/[\r\n]+/mg,' ').replace(/\s+/gm,''); +function strip(string) { + return string.replace(/[\r\n]+/mg,' ').replace(/\s+/gm,''); +} + +function read (file) { + return fs.readFileSync(file, 'utf8'); +} + +function write (file, data) { + fs.writeFileSync(file, data); } describe('inline-critical', function() { it('should inline css', function(done) { - var html = fs.readFileSync('test/fixtures/index.html', 'utf8'); - var css = fs.readFileSync('test/fixtures/critical.css', 'utf8'); + var html = read('test/fixtures/index.html'); + var css = read('test/fixtures/critical.css'); - var expected = fs.readFileSync('test/fixtures/index-inlined-async-final.html', 'utf8'); + var expected = read('test/fixtures/index-inlined-async-final.html'); var out = inlineCritical(html, css); - expect(stripWhitespace(out.toString('utf-8'))).to.be.equal(stripWhitespace(expected)); + expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); done(); }); it('should inline and minify css', function(done) { - var html = fs.readFileSync('test/fixtures/index.html', 'utf8'); - var css = fs.readFileSync('test/fixtures/critical.css', 'utf8'); + var html = read('test/fixtures/index.html'); + var css = read('test/fixtures/critical.css'); - var expected = fs.readFileSync('test/fixtures/index-inlined-async-minified-final.html', 'utf8'); - var out = inlineCritical(html, css, true); + var expected = read('test/fixtures/index-inlined-async-minified-final.html'); + var out = inlineCritical(html, css, { minify: true }); - expect(stripWhitespace(out.toString('utf-8'))).to.be.equal(stripWhitespace(expected)); + expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); + + done(); + }); + + + it('should inline and extract css', function(done) { + var html = read('test/fixtures/cartoon.html'); + var css = read('test/fixtures/critical.css'); + + write('test/fixtures/cartoon.css', read('test/fixtures/cartoon-src.css')); + + inlineCritical(html, css, { extract: true, basePath: 'test/fixtures' }); + + expect(read('test/fixtures/cartoon.css')).to.be.equal(read('test/fixtures/cartoon-expected.css')); done(); }); From 2a8d9c0775bb6f1cf1073f64d4f5b7bdfdd6158c Mon Sep 17 00:00:00 2001 From: Nicolas Bevacqua Date: Sat, 18 Oct 2014 22:16:34 -0300 Subject: [PATCH 2/4] documentation and indentation --- README.md | 13 +++++++++++++ test/index.js | 50 ++++++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index bbb1ea6..9783641 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,16 @@ var critical = fs.readFileSync('test/fixtures/critical.css', 'utf8'); var inlined = inline(html, critical); ``` + +## `inline(html, styles, options?) + +- `html` is the HTML you want to use to inline your critical styles, or any other styles +- `styles` are the styles you're looking to inline +- `options` is an optional configuration object + - `minify` will minify the styles before inlining + - `extract` will remove the inlined styles from any stylesheets referenced in the HTML + - `basePath` will be used when extracting styles to find the files references by `href` attributes + +## License + +MIT diff --git a/test/index.js b/test/index.js index b8a3180..df47788 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,5 @@ +'use strict'; + var expect = require('chai').expect; var fs = require('fs'); var inlineCritical = require('..'); @@ -15,42 +17,42 @@ function write (file, data) { } describe('inline-critical', function() { - it('should inline css', function(done) { - var html = read('test/fixtures/index.html'); - var css = read('test/fixtures/critical.css'); + it('should inline css', function(done) { + var html = read('test/fixtures/index.html'); + var css = read('test/fixtures/critical.css'); - var expected = read('test/fixtures/index-inlined-async-final.html'); - var out = inlineCritical(html, css); + var expected = read('test/fixtures/index-inlined-async-final.html'); + var out = inlineCritical(html, css); - expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); + expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); - done(); - }); + done(); + }); - it('should inline and minify css', function(done) { - var html = read('test/fixtures/index.html'); - var css = read('test/fixtures/critical.css'); + it('should inline and minify css', function(done) { + var html = read('test/fixtures/index.html'); + var css = read('test/fixtures/critical.css'); - var expected = read('test/fixtures/index-inlined-async-minified-final.html'); - var out = inlineCritical(html, css, { minify: true }); + var expected = read('test/fixtures/index-inlined-async-minified-final.html'); + var out = inlineCritical(html, css, { minify: true }); - expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); + expect(strip(out.toString('utf-8'))).to.be.equal(strip(expected)); - done(); - }); + done(); + }); - it('should inline and extract css', function(done) { - var html = read('test/fixtures/cartoon.html'); - var css = read('test/fixtures/critical.css'); + it('should inline and extract css', function(done) { + var html = read('test/fixtures/cartoon.html'); + var css = read('test/fixtures/critical.css'); - write('test/fixtures/cartoon.css', read('test/fixtures/cartoon-src.css')); + write('test/fixtures/cartoon.css', read('test/fixtures/cartoon-src.css')); - inlineCritical(html, css, { extract: true, basePath: 'test/fixtures' }); + inlineCritical(html, css, { extract: true, basePath: 'test/fixtures' }); - expect(read('test/fixtures/cartoon.css')).to.be.equal(read('test/fixtures/cartoon-expected.css')); + expect(read('test/fixtures/cartoon.css')).to.be.equal(read('test/fixtures/cartoon-expected.css')); - done(); - }); + done(); + }); }); From 469b70476bb1062534dc1f82ff560afd88e841bf Mon Sep 17 00:00:00 2001 From: Nicolas Bevacqua Date: Sat, 18 Oct 2014 22:22:08 -0300 Subject: [PATCH 3/4] fix jshint :) --- .jshintrc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.jshintrc b/.jshintrc index fcb5338..5b0ff71 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,3 +1,8 @@ { - "laxcomma": true + "laxcomma": true, + "globals": { + "it": false, + "describe": false + }, + "node": true } From 28f78aae21d48ab793fdc59fab87a33506571eff Mon Sep 17 00:00:00 2001 From: Nicolas Bevacqua Date: Mon, 20 Oct 2014 11:44:53 -0300 Subject: [PATCH 4/4] revving --- index.js | 20 +- package.json | 3 +- test/fixtures/cartoon-src.css | 306 ----------------------------- test/fixtures/cartoon.18d89c7f.css | 15 ++ test/fixtures/cartoon.css | 291 +++++++++++++++++++++++++++ test/index.js | 6 +- 6 files changed, 324 insertions(+), 317 deletions(-) delete mode 100644 test/fixtures/cartoon-src.css create mode 100644 test/fixtures/cartoon.18d89c7f.css diff --git a/index.js b/index.js index 787c705..2392844 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ var fs = require('fs'); var path = require('path'); var cave = require('cave'); +var reaver = require('reaver'); var cheerio = require('cheerio'); var CleanCSS = require('clean-css'); @@ -33,12 +34,8 @@ module.exports = function(html, styles, options) { // insert noscript block right after stylesheets links.eq(0).first().after(noscript); - // wrap links to stylesheets in noscript block so that they will evaluated when js is turned off var hrefs = links.map(function(idx, el) { - el = $(el); - noscript.append(el); - noscript.append('\n'); - return el.attr('href'); + return $(this).attr('href'); }).toArray(); // extract styles from stylesheets if extract option is set @@ -46,16 +43,25 @@ module.exports = function(html, styles, options) { if (!o.basePath) { throw new Error('Option `basePath` is missing and required when using `extract`!'); } - hrefs.forEach(function(href) { + hrefs = hrefs.map(function(href) { var file = path.resolve(o.basePath, href); if (!fs.existsSync(file)) { return; } var diff = cave(file, { css: styles }); - fs.writeFileSync(file, diff); + fs.writeFileSync(reaver.rev(file, diff), diff); + return reaver.rev(href, diff); }); } + // 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'); + }); + // build js block to load blocking stylesheets $('body').append('