Skip to content
This repository was archived by the owner on Oct 21, 2022. It is now read-only.

Commit bee65d1

Browse files
committed
commit c06509eaeb2aec1639c8ce9b197791687925ee8e
Author: Jeffrey Lembeck <jlembeck@gmail.com> Date: Wed Jun 25 18:31:26 2014 -0700 Add readme, move some things .gitignore Make a node wrapper commit cbcc6930f2cb65d949a39ae66b3d39ee4998c7a6 Author: Jeffrey Lembeck <jlembeck@gmail.com> Date: Wed Jun 25 18:15:17 2014 -0700 You can run the phantomjs task now and have it work, still needed: take arguments, convert to grunt task
0 parents  commit bee65d1

11 files changed

Lines changed: 425 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.jshintrc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"curly": true,
3+
"eqeqeq": true,
4+
"immed": true,
5+
"latedef": true,
6+
"newcap": true,
7+
"noarg": true,
8+
"sub": true,
9+
"undef": true,
10+
"unused": true,
11+
"boss": true,
12+
"eqnull": true,
13+
"node": true,
14+
"es5": true
15+
}

Gruntfile.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*global module:true*/
2+
(function(){
3+
'use strict';
4+
5+
module.exports = function(grunt) {
6+
7+
// Project configuration.
8+
grunt.initConfig({
9+
nodeunit: {
10+
files: ['test/**/*_test.js']
11+
},
12+
jshint: {
13+
options: {
14+
jshintrc: '.jshintrc'
15+
},
16+
gruntfile: {
17+
src: 'Gruntfile.js'
18+
},
19+
lib: {
20+
src: ['lib/**/*.js']
21+
},
22+
test: {
23+
src: ['test/**/*.js']
24+
}
25+
},
26+
watch: {
27+
gruntfile: {
28+
files: '<%= jshint.gruntfile.src %>',
29+
tasks: ['jshint:gruntfile']
30+
},
31+
lib: {
32+
files: '<%= jshint.lib.src %>',
33+
tasks: ['jshint:lib', 'nodeunit']
34+
},
35+
test: {
36+
files: '<%= jshint.test.src %>',
37+
tasks: ['jshint:test', 'nodeunit']
38+
}
39+
}
40+
});
41+
42+
// These plugins provide necessary tasks.
43+
grunt.loadNpmTasks('grunt-contrib-nodeunit');
44+
grunt.loadNpmTasks('grunt-contrib-jshint');
45+
grunt.loadNpmTasks('grunt-contrib-watch');
46+
47+
// Default task.
48+
grunt.registerTask('default', ['jshint', 'nodeunit']);
49+
50+
};
51+
}());
52+

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CriticalCSS
2+
3+
To Run: `phantomjs lib/criticalrunner.js`

critical.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*global require:true*/
2+
/*global console:true*/
3+
(function( exports ){
4+
"use strict";
5+
6+
var phantomJsPath = require('phantomjs').path;
7+
var execFile = require('child_process').execFile;
8+
var path = require( 'path' );
9+
10+
exports.findCritical = function( url, opts, cb ){
11+
var defaultCb = function( err, output ){
12+
if( err ){
13+
throw new Error( err );
14+
} else {
15+
console.log( output );
16+
}
17+
};
18+
19+
if( typeof url !== "string" ){
20+
throw new TypeError( "URL must be a string" );
21+
}
22+
23+
if( typeof opts === "undefined" && typeof cb === "undefined" ){
24+
opts = {};
25+
cb = defaultCb;
26+
}
27+
28+
if( typeof opts === "function" ){
29+
cb = opts;
30+
opts = {};
31+
}
32+
33+
var width = opts.width || 1200;
34+
var height = opts.height || 900;
35+
var output = opts.output || "dist.css";
36+
var filename = opts.filename || "all.css";
37+
38+
39+
execFile( phantomJsPath,
40+
[
41+
path.join( 'lib', 'criticalrunner.js' ),
42+
url,
43+
filename,
44+
output,
45+
width,
46+
height
47+
],
48+
49+
function(err, stdout, stderr){
50+
console.log( "Hi" );
51+
if( err ){
52+
console.log("\nSomething went wrong with phantomjs...");
53+
if( stderr ){
54+
console.log( stderr );
55+
}
56+
cb( err, null );
57+
} else {
58+
console.log( stdout );
59+
cb( null, stdout );
60+
}
61+
62+
}
63+
);
64+
65+
};
66+
67+
}(typeof exports === 'object' && exports || this));

lib/criticalcss.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
criticalCSS by @scottjehl. Run this on your CSS, get the styles that are applicable in the viewport (critical). The url arg should be any part of the URL of the stylesheets you'd like to parse. So, 'all.css' or '/css/' would both work.
3+
*/
4+
(function(exports){
5+
"use strict";
6+
/**
7+
* opts
8+
* opts.sheets = document.styleSheets
9+
* opts.maxTop = window.innerHeight
10+
* opts.querySelector = function( selector ){
11+
* return page.evaluate(function(s){
12+
* return document.querySelector( s );
13+
* },selector);
14+
* opts.matchMedia = window.matchMedia
15+
*/
16+
var criticalCSS = function( url, opts ){
17+
var sheets = opts.sheets,
18+
maxTop = opts.maxTop,
19+
critical = [];
20+
21+
var removeNull = function( el ){
22+
return el !== null;
23+
};
24+
25+
var critCSSText;
26+
var aboveFold = function( rule ){
27+
if( !rule.selectorText ){
28+
return false;
29+
}
30+
var selectors = rule.selectorText.split(","),
31+
criticalSelectors = [];
32+
for( var k = 0, sl = selectors.length; k < sl; k++ ) {
33+
var elem = opts.querySelector( selectors[ k ] );
34+
if( elem && elem.offsetTop <= maxTop ){
35+
criticalSelectors.push( selectors[ k ] );
36+
}
37+
}
38+
if( criticalSelectors.length ){
39+
return criticalSelectors.join(",") + rule.cssText.match( /\{.+/ );
40+
}
41+
else {
42+
return false;
43+
}
44+
};
45+
46+
sheets = Array.prototype.filter.call(sheets, removeNull);
47+
48+
for( var i = 0, l = sheets.length; i < l; i++ ){
49+
var sheet = sheets[ i ],
50+
href = sheet.href,
51+
rules = sheet.rules;
52+
53+
rules = Array.prototype.filter.call( rules, removeNull );
54+
55+
if( url && href && href.indexOf( url ) > -1 ){
56+
for( var j = 0, rl = rules.length; j < rl; j++ ){
57+
58+
var media = rules[ j ].media,
59+
matchingRules = [];
60+
if( media && opts.matchMedia( media.mediaText ).matches ){
61+
var innerRules = rules[ j ].rules;
62+
for( var k in innerRules ){
63+
critCSSText = aboveFold( innerRules[ k ] );
64+
if( critCSSText ){
65+
matchingRules.push( critCSSText );
66+
}
67+
}
68+
if( matchingRules.length ){
69+
matchingRules.unshift( "@media " + media.mediaText + "{" );
70+
matchingRules.push( "}" );
71+
}
72+
73+
} else if( !media ){
74+
critCSSText = aboveFold( rules[ j ] );
75+
if( critCSSText && !critCSSText.match(/^\s+$/) ){
76+
matchingRules.push( critCSSText );
77+
}
78+
}
79+
critical.push( matchingRules.join( "\n" ) );
80+
}
81+
82+
}
83+
}
84+
return critical.join( "\n" );
85+
};
86+
87+
exports.criticalCSS = criticalCSS;
88+
89+
}(typeof exports === 'object' && exports || this));

lib/criticalrunner.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*global phantom:true*/
2+
/*global require:true*/
3+
/*global window:true*/
4+
5+
/*
6+
phantom args sent from critical.js:
7+
[0] - url
8+
[1] - filename
9+
[2] - output filepath
10+
[3] - width of page
11+
[4] - height of page
12+
*/
13+
(function(){
14+
"use strict";
15+
16+
var site = phantom.args[0],
17+
filename = phantom.args[1],
18+
output = phantom.args[2],
19+
width = phantom.args[3],
20+
height = phantom.args[4];
21+
22+
23+
var system = require( "system" );
24+
var fs = require( "fs" );
25+
26+
var errorHandler = function(msg, trace) {
27+
var msgStack = ['PHANTOM ERROR: ' + msg];
28+
if (trace && trace.length) {
29+
msgStack.push('TRACE:');
30+
trace.forEach(function(t) {
31+
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
32+
});
33+
}
34+
system.stderr.write( msgStack.join('\n') );
35+
phantom.exit(1);
36+
};
37+
38+
39+
var page = require( "webpage" ).create();
40+
var cc = require( "./criticalCSS" );
41+
42+
phantom.onError = errorHandler;
43+
page.onError = errorHandler;
44+
45+
46+
page.viewportSize = { width: width , height: height };
47+
48+
page.open( site, function( status ){
49+
if( status === "success" ){
50+
var stylesheets = page.evaluate(function(){
51+
return window.document.styleSheets;
52+
});
53+
54+
var maxTop = page.evaluate(function(){
55+
return window.innerHeight;
56+
});
57+
58+
var opts = {
59+
sheets: stylesheets,
60+
maxTop: maxTop,
61+
querySelector: function( selector ){
62+
return page.evaluate( function(s){
63+
return window.document.querySelector( s );
64+
}, selector );
65+
},
66+
matchMedia: function( query ){
67+
return page.evaluate( function(q){
68+
return window.matchMedia( q );
69+
}, query );
70+
}
71+
};
72+
73+
var crit = cc.criticalCSS( filename, opts );
74+
if( crit.length === 0 ){
75+
throw new Error( "criticalCSS didn't run or no critical css, that seems unlikely" );
76+
} else {
77+
fs.write(output, crit, 'w');
78+
phantom.exit();
79+
}
80+
} else {
81+
throw new Error( "Page didn't open" );
82+
}
83+
});
84+
85+
}());

package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "CriticalCSS",
3+
"description": "Finds the Above the Fold CSS for your page, and outputs it into a file",
4+
"version": "0.6.0",
5+
"homepage": "https://github.com/filamentgroup/criticalcss",
6+
"author": {
7+
"name": "Scott Jehl/Jeffrey Lembeck/Filament Group",
8+
"email": "hello@filamentgroup.com",
9+
"url": "http://filamentgroup.com"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/filamentgroup/criticalcss.git"
14+
},
15+
"bugs": {
16+
"url": "https://github.com/filamentgroup/criticalcss/issues"
17+
},
18+
"licenses": [
19+
{
20+
"type": "MIT",
21+
"url": "https://github.com/filamentgroup/criticalcss/blob/master/LICENSE-MIT"
22+
}
23+
],
24+
"engines": {
25+
"node": ">= 0.10.0"
26+
},
27+
"scripts": {
28+
"test": "grunt nodeunit"
29+
},
30+
"dependencies": {
31+
"phantomjs": "1.9.7-9"
32+
},
33+
"devDependencies": {
34+
"grunt-contrib-jshint": "~0.1.1",
35+
"grunt-contrib-nodeunit": "~0.1.2",
36+
"grunt-contrib-qunit": "~0.3.0",
37+
"grunt-contrib-watch": "~0.2.0",
38+
"grunt": "~0.4.5",
39+
"phantom-unit": "0.1.1"
40+
},
41+
"keywords": []
42+
}

test/files/all.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
h1 {
2+
font-size: 2em;
3+
}
4+
p {
5+
font-size: 1.5em;
6+
font-weight: bold;
7+
}

test/files/test-site.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<html>
2+
<head>
3+
<title>Test Page for CriticalCSS</title>
4+
<link href="./all.css" rel="stylesheet" />
5+
</head>
6+
<body>
7+
<h1>Hi</h1>
8+
<p>This is some content</p>
9+
</body>
10+
</html>

0 commit comments

Comments
 (0)