critical
Advanced tools
Comparing version 0.1.3 to 0.1.4
175
index.js
@@ -10,8 +10,8 @@ /* | ||
'use strict'; | ||
var oust = require('oust'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var penthouse = require('penthouse'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var inliner = require('./inline-styles'); | ||
var CleanCSS = require('clean-css'); | ||
var CleanCSS = require('clean-css'); | ||
var oust = require('oust'); | ||
var inliner = require('./inline-styles'); | ||
@@ -26,7 +26,6 @@ /** | ||
opts = opts || {}; | ||
cb = cb || function () {}; | ||
cb = cb || function () {}; | ||
if (!opts.src && !opts.base) { | ||
cb(new Error('A valid source and base path are required.')); | ||
return; | ||
if (!opts.src || !opts.base) { | ||
throw new Error('A valid source and base path are required.'); | ||
} | ||
@@ -41,32 +40,48 @@ | ||
} | ||
var url = path.join(process.cwd(), opts.base + opts.src); | ||
fs.readFile(url, function (err, html){ | ||
if (err) throw err; | ||
// Oust extracts a list of your stylesheets | ||
var hrefs = oust(html, 'stylesheets'); | ||
// Penthouse then determines your critical | ||
// path CSS using these as input. | ||
penthouse({ | ||
url : url, | ||
css : path.join(process.cwd(), opts.base + hrefs[0]), | ||
// What viewports do you care about? | ||
width : opts.width, // viewport width | ||
height : opts.height // viewport height | ||
}, function (err, criticalCSS) { | ||
if(opts.minify === true){ | ||
var minimized = new CleanCSS().minify(criticalCSS); | ||
criticalCSS = minimized; | ||
} | ||
if(opts.dest){ | ||
// Write critical-path CSS | ||
fs.writeFile(path.join(process.cwd(), opts.base + opts.dest), criticalCSS, function (err){ | ||
cb(err, criticalCSS) | ||
}); | ||
} else { | ||
cb(err, criticalCSS); | ||
} | ||
}); | ||
}); | ||
} | ||
var url = path.join(opts.base, opts.src); | ||
fs.readFile(url, function (err, html) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
// Oust extracts a list of your stylesheets | ||
var hrefs = oust(html, 'stylesheets'); | ||
// Penthouse then determines your critical | ||
// path CSS using these as input. | ||
penthouse({ | ||
url: url, | ||
css: path.join(opts.base, hrefs[0]), | ||
// What viewports do you care about? | ||
width: opts.width, // viewport width | ||
height: opts.height // viewport height | ||
}, function (err, criticalCSS) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
if (opts.minify === true) { | ||
criticalCSS = new CleanCSS().minify(criticalCSS); | ||
} | ||
if (opts.dest) { | ||
// Write critical-path CSS | ||
fs.writeFile(path.join(opts.base, opts.dest), criticalCSS, function (err) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
cb(null, criticalCSS.toString()); | ||
}); | ||
} else { | ||
cb(null, criticalCSS.toString()); | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
@@ -79,27 +94,34 @@ * Critical path CSS inlining | ||
exports.inline = function (opts, cb) { | ||
opts = opts || {}; | ||
cb = cb || function () {}; | ||
opts = opts || {}; | ||
cb = cb || function () {}; | ||
if (!opts.src && !opts.base) { | ||
cb(new Error('A valid source and base path are required.')); | ||
return; | ||
} | ||
var url = opts.base + opts.src; | ||
// Inline the critical path CSS | ||
fs.readFile(url, function (err, data){ | ||
if (err) throw err; | ||
var out = inliner(data, opts.base, opts.minify); | ||
if (opts.dest){ | ||
// Write HTML with inlined CSS to dest | ||
fs.writeFile(path.join(process.cwd(), opts.base + opts.dest), out, function (err) { | ||
cb(err, out); | ||
}); | ||
} else { | ||
cb(err, out); | ||
if (!opts.src || !opts.base) { | ||
throw new Error('A valid source and base path are required.'); | ||
} | ||
}); | ||
} | ||
// Inline the critical path CSS | ||
fs.readFile(path.join(opts.base, opts.src), function (err, data) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
var out = inliner(data, opts.base, opts.minify); | ||
if (opts.dest) { | ||
// Write HTML with inlined CSS to dest | ||
fs.writeFile(path.join(opts.base, opts.dest), out, function (err) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
cb(null, out.toString()); | ||
}); | ||
} else { | ||
cb(null, out.toString()); | ||
} | ||
}); | ||
}; | ||
/** | ||
@@ -112,16 +134,23 @@ * Generate and inline critical-path CSS | ||
exports.generateInline = function (opts, cb) { | ||
opts = opts || {}; | ||
cb = cb || function () {}; | ||
if (!opts.styleTarget && !opts.htmlTarget) { | ||
cb(new Error('Valid style and HTML targets are required.')); | ||
return; | ||
} | ||
var genOpts = opts, inlineOpts = opts; | ||
genOpts.dest = opts.styleTarget; | ||
exports.generate(genOpts, function (err, output) { | ||
if (err) cb(err); | ||
inlineOpts.dest = opts.htmlTarget; | ||
exports.inline(inlineOpts); | ||
}); | ||
} | ||
opts = opts || {}; | ||
cb = cb || function () {}; | ||
if (!opts.styleTarget || !opts.htmlTarget) { | ||
throw new Error('Valid style and HTML targets are required.'); | ||
} | ||
var genOpts = opts; | ||
var inlineOpts = opts; | ||
genOpts.dest = opts.styleTarget; | ||
exports.generate(genOpts, function (err, output) { | ||
if (err) { | ||
cb(err); | ||
return; | ||
} | ||
inlineOpts.dest = opts.htmlTarget; | ||
exports.inline(inlineOpts); | ||
}); | ||
}; |
@@ -1,39 +0,39 @@ | ||
var cheerio = require('cheerio') | ||
var path = require('path') | ||
var cheerio = require('cheerio'); | ||
var path = require('path'); | ||
// Fork of inline-styles with minification support | ||
var fs = require('fs') | ||
var url = require('url') | ||
var inliner = require('imageinliner') | ||
var CleanCSS = require('clean-css') | ||
var fs = require('fs'); | ||
var url = require('url'); | ||
var inliner = require('imageinliner'); | ||
var CleanCSS = require('clean-css'); | ||
module.exports = function(html, base, minify) { | ||
base = base || process.cwd() | ||
var dom = cheerio.load(String(html)) | ||
injectStyles(dom) | ||
return new Buffer(dom.html()) | ||
base = base || process.cwd(); | ||
var dom = cheerio.load(String(html)); | ||
injectStyles(dom); | ||
return new Buffer(dom.html()); | ||
function injectStyles(dom) { | ||
var styles = []; | ||
dom('link').each(function(idx, el) { | ||
el = dom(el) | ||
var href = el.attr('href') | ||
el = dom(el); | ||
var href = el.attr('href'); | ||
if (el.attr('rel') === 'stylesheet' && isLocal(href)) { | ||
var dir = path.dirname(href) | ||
var file = path.join(base, href) | ||
var style = fs.readFileSync(file) | ||
var inlined = inliner.css(style.toString(), { cssBasePath: dir }) | ||
var inlinedStyles = inlined.toString() | ||
if(minify) { | ||
inlinedStyles = new CleanCSS().minify(inlinedStyles) | ||
var dir = path.dirname(href); | ||
var file = path.join(base, href); | ||
var style = fs.readFileSync(file); | ||
var inlined = inliner.css(style.toString(), { cssBasePath: dir }); | ||
var inlinedStyles = inlined.toString(); | ||
if (minify) { | ||
inlinedStyles = new CleanCSS().minify(inlinedStyles); | ||
} | ||
var inlinedTag = "<style>\n" + inlinedStyles + '\n</style>' | ||
el.replaceWith(inlinedTag) | ||
var inlinedTag = "<style>\n" + inlinedStyles + '\n</style>'; | ||
el.replaceWith(inlinedTag); | ||
} | ||
}) | ||
} | ||
function isLocal(href) { | ||
return href && !url.parse(href).hostname; | ||
} | ||
} | ||
}; |
{ | ||
"name": "critical", | ||
"version": "0.1.3", | ||
"description": "Critical-path CSS generation & inlining", | ||
"main": "index.js", | ||
"version": "0.1.4", | ||
"description": "Extract & Inline Critical-path CSS from HTML", | ||
"author": "Addy Osmani", | ||
"license": "Apache-2.0", | ||
"repository": "addyosmani/critical", | ||
"scripts": { | ||
@@ -22,14 +24,12 @@ "test": "mocha test.js --timeout 15000" | ||
}, | ||
"author": "Addy Osmani", | ||
"license": "Apache2", | ||
"dependencies": { | ||
"oust": "0.1.1", | ||
"penthouse": "^0.2.1", | ||
"clean-css": "~2.2.4", | ||
"cheerio": "^0.17.0", | ||
"imageinliner": "^0.2.2" | ||
"clean-css": "^2.2.4", | ||
"imageinliner": "^0.2.2", | ||
"oust": "^0.2.0", | ||
"penthouse": "^0.2.1" | ||
}, | ||
"devDependencies": { | ||
"mocha": "^1.20.1" | ||
"mocha": "*" | ||
} | ||
} |
169
README.md
@@ -1,12 +0,14 @@ | ||
critical | ||
======== | ||
# critical [![Build Status](https://travis-ci.org/addyosmani/critical.svg?branch=master)](https://travis-ci.org/addyosmani/critical) | ||
> Critical Path CSS generation & inlining | ||
> Extract & Inline Critical-path CSS from HTML | ||
## Installation | ||
A module by [@addyosmani](http://github.com/addyosmani) and [@sindresorhus](http://github.com/sindresorhus) | ||
## Install | ||
```sh | ||
$ npm install --save critical | ||
``` | ||
npm install -g critical | ||
``` | ||
## Usage | ||
@@ -16,7 +18,7 @@ | ||
```sh | ||
```js | ||
var critical = require('critical'); | ||
``` | ||
###Generate and inline critical-path CSS | ||
### Generate and inline critical-path CSS | ||
@@ -35,3 +37,3 @@ ```js | ||
###Generate critical-path CSS | ||
### Generate critical-path CSS | ||
@@ -42,8 +44,8 @@ Basic usage: | ||
critical.generate({ | ||
base: 'test/', | ||
src: 'index.html', | ||
dest: 'styles/main.css', | ||
width: 320, | ||
height: 480, | ||
}); | ||
base: 'test/', | ||
src: 'index.html', | ||
dest: 'styles/main.css', | ||
width: 320, | ||
height: 480 | ||
}); | ||
``` | ||
@@ -55,9 +57,9 @@ | ||
critical.generate({ | ||
base: 'test/', | ||
src: 'index.html', | ||
width: 320, | ||
dest: 'styles/styles.min.css', | ||
minify: true, | ||
height: 480 | ||
}); | ||
base: 'test/', | ||
src: 'index.html', | ||
dest: 'styles/styles.min.css', | ||
minify: true, | ||
width: 320, | ||
height: 480 | ||
}); | ||
``` | ||
@@ -69,13 +71,13 @@ | ||
critical.generate({ | ||
base: 'test/', | ||
src: 'index.html', | ||
width: 320, | ||
height: 480, | ||
}, function (err, output){ | ||
// You now have critical-path CSS | ||
// Works with and without dest specified | ||
}); | ||
base: 'test/', | ||
src: 'index.html', | ||
width: 320, | ||
height: 480 | ||
}, function (err, output) { | ||
// You now have critical-path CSS | ||
// Works with and without dest specified | ||
}); | ||
``` | ||
###Inline `<style>` / critical CSS from generation | ||
### Inline `<style>` / critical CSS from generation | ||
@@ -86,6 +88,6 @@ Basic usage: | ||
critical.inline({ | ||
base: 'test/', | ||
src: 'index-critical.html', | ||
dest: 'inlined.html' | ||
}); | ||
base: 'test/', | ||
src: 'index-critical.html', | ||
dest: 'inlined.html' | ||
}); | ||
``` | ||
@@ -97,7 +99,7 @@ | ||
critical.inline({ | ||
base: 'test/', | ||
src: 'index-critical.html', | ||
dest: 'inlined-minified.html', | ||
minify: true | ||
}); | ||
base: 'test/', | ||
src: 'index-critical.html', | ||
dest: 'inlined-minified.html', | ||
minify: true | ||
}); | ||
``` | ||
@@ -109,52 +111,24 @@ | ||
critical.inline({ | ||
base: 'test/', | ||
src: 'index-critical.html', | ||
}, function (err, output){ | ||
// You now have HTML with inlined critical-path CSS | ||
// Works with and without dest specified | ||
}); | ||
base: 'test/', | ||
src: 'index-critical.html' | ||
}, function (err, output){ | ||
// You now have HTML with inlined critical-path CSS | ||
// Works with and without dest specified | ||
}); | ||
``` | ||
###Options | ||
### Options | ||
####base | ||
Type: `String` | ||
| Name | Type | Description | | ||
| ------------- | ------------- | ------------- | | ||
| base | `string` | Base directory in which the source and destination are to be written | | ||
| src | `string` | Location of the HTML source to be operated against | | ||
| dest | `string` | Location of where to save the output of an operation | | ||
| width | `integer` | (Generation only) Width of the target viewport | | ||
| height | `integer` | (Generation only) Height of the target viewport | | ||
| minify | `boolean` | Enable minification of CSS output | | ||
| styleTarget | `string` | (`generateInline` only) Destination for critical-path styles | | ||
| htmlTarget | `string` | (`generateInline` only) Destination for (critical-path CSS) style-inlined HTML | | ||
Base directory in which the source and destination are to be written. | ||
####src | ||
Type: `String` | ||
Location of the HTML source to be operated against. | ||
####dest | ||
Type: `String` | ||
Location of where to save the output of an operation. | ||
####width | ||
Type: `integer` | ||
(Generation only) Width of the target viewport. | ||
####height | ||
Type: `integer` | ||
(Generation only) Height of the target viewport. | ||
####minify | ||
Type: `boolean` | ||
Enable minification of CSS output | ||
####styleTarget | ||
Type: `string` | ||
(generateInline only) Destination for critical-path styles | ||
####htmlTarget | ||
Type: `string` | ||
(generateInline only) Destination for (critical-path CSS) style-inlined HTML | ||
## Why? | ||
@@ -164,14 +138,31 @@ | ||
> CSS is required to construct the render tree for your pages and JavaScript will often block on CSS during initial construction of the page. You should ensure that any non-essential CSS is marked as non-critical (e.g. print and other media queries), and that the amount of critical CSS and the time to deliver it is as small as possible. | ||
> CSS is required to construct the render tree for your pages and JavaScript | ||
will often block on CSS during initial construction of the page. | ||
You should ensure that any non-essential CSS is marked as non-critical | ||
(e.g. print and other media queries), and that the amount of critical CSS | ||
and the time to deliver it is as small as possible. | ||
### Why should critical-path CSS be inlined? | ||
> For best performance, you may want to consider inlining the critical CSS directly into the HTML document. This eliminates additional roundtrips in the critical path and if done correctly can be used to deliver a “one roundtrip” critical path length where only the HTML is a blocking resource. | ||
> For best performance, you may want to consider inlining the critical CSS | ||
directly into the HTML document. This eliminates additional roundtrips | ||
in the critical path and if done correctly can be used to deliver a | ||
“one roundtrip” critical path length where only the HTML is a blocking resource. | ||
## FAQ | ||
### Are there any sample projects available using Critical? | ||
Why, yes!. Take a look at [this](https://github.com/addyosmani/critical-path-css-demo) Gulp project | ||
which demonstrates using Critical to generate and inline critical-path CSS. | ||
### When should I just use Penthouse directly? | ||
I recommend using Penthouse directly if your app has a large number of styles or stylesheets being dynamically injected into the DOM. Critical is best used when your page uses a fixed set of stylesheets as we can automatically scrape this for you, avoiding the overhead of passing known styles yourself manually to Penthouse. | ||
I recommend using [Penthouse](http://npmjs.org/package/penthouse) directly if your app has a large number of styles | ||
or stylesheets being dynamically injected into the DOM. Critical is best used | ||
when your page uses a fixed set of stylesheets as we can automatically scrape | ||
this for you, avoiding the overhead of passing known styles yourself manually to Penthouse. | ||
## License | ||
@@ -181,3 +172,1 @@ | ||
Copyright 2014 Google Inc | ||
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
9841
163
163
+ Addedboolbase@1.0.0(transitive)
+ Addedcheerio@0.19.0(transitive)
+ Addedcss-select@1.0.0(transitive)
+ Addedcss-what@1.0.0(transitive)
+ Addeddom-serializer@0.1.1(transitive)
+ Addeddomelementtype@1.3.1(transitive)
+ Addeddomhandler@2.3.0(transitive)
+ Addedhtmlparser2@3.8.3(transitive)
+ Addedlodash@3.10.1(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addednth-check@1.0.2(transitive)
+ Addedoust@0.2.4(transitive)
- Removedminimist@0.1.0(transitive)
- Removedoust@0.1.1(transitive)
Updatedclean-css@^2.2.4
Updatedoust@^0.2.0