Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite using promises and less dependencies #23

Closed
wants to merge 14 commits into from
5 changes: 3 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"eol-last": 2
},
"env": {
"node": true
"node": true,
"es6": true
},
"extends": "eslint:recommended"
}
}
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/test
6 changes: 1 addition & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"url": "https://github.com/jrit/web-resource-inliner.git"
},
"engines": {
"node": ">=4.0.0"
"node": ">=4.2.0"
},
"devDependencies": {
"faux-jax": "^5.0.0",
Expand All @@ -32,13 +32,9 @@
"mocha": "^2.0.1"
},
"dependencies": {
"async": "^0.9.0",
"chalk": "^1.1.3",
"clean-css": "1.1.7",
"datauri": "~0.2.0",
"htmlparser2": "^3.9.0",
"lodash.constant": "^3.0.0",
"lodash.unescape": "^4.0.0",
"request": "^2.72.0",
"xtend": "^4.0.0"
},
Expand Down
160 changes: 85 additions & 75 deletions src/css.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,107 @@
"use strict";

var CleanCSS = require( "clean-css" );
var xtend = require( "xtend" );
var async = require( "async" );
var path = require( "path" );
var constant = require( "lodash.constant" );
var inline = require( "./util" );

var search = require( "./util/search" );
var isFunction = require( "./util/isFunction" );
var isBase64Path = require( "./util/isBase64Path" );
var isCIDPath = require( "./util/isCIDPath" );

var isRemotePath = require( "./util/isRemotePath" );
var getFileReplacement = require( "./util/getFileReplacement" );

module.exports = function( options, callback )
{
var settings = xtend( {}, inline.defaults, options );

var replaceUrl = function( callback )
{
var args = this;
options = require( "./options" )( options );
var urlRegex = /url\(\s*["']?\s*([^)'"]+)\s*["']?\s*\);\s*(\/\*[^\*]+\*\/)?/gi;

if( inline.isBase64Path( args.src ) )
{
return callback( null ); // Skip
function validateAttribute( attr ) {
function exp( str ) {
return new RegExp( "\\/\\*\\s*" + str + "\\s*\\*\\/", "i" );
}
if ( attr ) {
return ( !exp( options.inlineAttribute + "-ignore" ).test( attr ) &&
options.images || exp( options.inlineAttribute ).test( attr ) );
}
return true;
}

inline.getFileReplacement( args.src, settings, function( err, datauriContent )
{
if( err )
{
return inline.handleReplaceErr( err, args.src, settings.strict, callback );
}
if( typeof( args.limit ) === "number" && datauriContent.length > args.limit * 1000 )
{
return callback( null ); // Skip
}

var css = "url(\"" + datauriContent + "\");";
var re = new RegExp( "url\\(\\s?[\"']?(" + inline.escapeSpecialChars( args.src ) + ")[\"']?\\s?\\);", "g" );
result = result.replace( re, constant( css ) );

return callback( null );
function replace( src, content ) {
var replacement = src.replace( urlRegex, function( match, p1 ) {
return match.replace( p1, content );
} );
};

var rebase = function( src )
{
var css = "url(\"" + path.join( settings.rebaseRelativeTo, src ).replace( /\\/g, "/" ) + "\");";
var re = new RegExp( "url\\(\\s?[\"']?(" + inline.escapeSpecialChars( src ) + ")[\"']?\\s?\\);", "g" );
result = result.replace( re, constant( css ) );
};

var result = settings.fileContent;
var tasks = [];
var found = null;

var urlRegex = /url\(\s?["']?([^)'"]+)["']?\s?\);.*/gi;
return options.fileContent =
options.fileContent.replace( src, replacement );
}

if( settings.rebaseRelativeTo )
{
var matches = {};
while( ( found = urlRegex.exec( result ) ) !== null )
{
var src = found[ 1 ];
matches[ src ] = true;
var promise = new Promise( function( resolve, reject ) {
if ( !options.fileContent ) {
return reject( new Error( "No file content" ) );
}

for( var src in matches )
{
if( !inline.isRemotePath( src ) && !inline.isBase64Path( src ) )
{
rebase( src );
return resolve(
search( urlRegex, options.fileContent
).reduce( function( result, src ) {
if ( !validateAttribute( src[2] ) ||
isBase64Path( src[1] ) ||
isCIDPath( src[1] ) ) {
return result;
}
}
}

var inlineAttributeCommentRegex = new RegExp( "\\/\\*\\s?" + settings.inlineAttribute + "\\s?\\*\\/", "i" );
var inlineAttributeIgnoreCommentRegex = new RegExp( "\\/\\*\\s?" + settings.inlineAttribute + "-ignore\\s?\\*\\/", "i" );
if ( !result[ src[1] ] ) {
result[ src[1] ] = [ src[0] ];
} else {
result[ src[1] ].push( src[0] );
}
return result;
}, {} ) );
} ).then( function( matches ) {
return Promise.all( Object.keys( matches ).map( function( src ) {
return new Promise( function( resolve, reject ) {
var origin = src;
if ( !isRemotePath( src ) && options.rebaseRelativeTo ) {
origin = path.join( options.rebaseRelativeTo, src ).replace( /\\/g, "/" );
}

// Replace source
return getFileReplacement( origin, options, function( err, content ) {
if ( err ) {
if ( options.strict ) {
return reject( err );
} else {
console.warn( "Not found, skipping: " + src );
return resolve(); // Skip
}
}
resolve( content );
} );
} ).then( function( content ) {
if ( !content || typeof( options.images ) === "number" &&
content.length > options.images * 1000 ) {
return; // Skip
}

while( ( found = urlRegex.exec( result ) ) !== null )
{
if( !inlineAttributeIgnoreCommentRegex.test( found[ 0 ] ) &&
( settings.images || inlineAttributeCommentRegex.test( found[ 0 ] ) ) )
{
tasks.push( replaceUrl.bind(
{
src: found[ 1 ],
limit: settings.images
} ) );
return Promise.all( matches[src].map( function( src ) {
return replace( src, content );
} ) );
} );
} ) );
} ).then( function() {
options.fileContent = options.cssmin ? CleanCSS.process( options.fileContent ) : options.fileContent;
if ( isFunction( callback ) ) {
callback( null, options.fileContent );
}
}

async.parallel( tasks, function( err )
{
if( !err )
{
result = settings.cssmin ? CleanCSS.process( result ) : result;
return options.fileContent;
} ).catch( function( err ) {
if ( isFunction( callback ) ) {
callback( err );
}
callback( err, result );
return Promise.reject( err );
} );

if ( !isFunction( callback ) ) {
return promise;
}
};
Loading