Skip to content

Instantly share code, notes, and snippets.

@jcreamer898
Created March 15, 2013 16:00
Show Gist options
  • Save jcreamer898/5170946 to your computer and use it in GitHub Desktop.
Save jcreamer898/5170946 to your computer and use it in GitHub Desktop.
contains bug fixes for anvil.compass
/*
anvil.compass - Compass plugin for anvil.js
version: 0.1.1
author: [object Object]
copyright: 2012
license: Dual licensed
MIT (http://www.opensource.org/licenses/mit-license)
GPL (http://www.opensource.org/licenses/gpl-license)
*/
var cp = require( "child_process" ),
spawn = cp.spawn,
exec = cp.exec,
path = require( "path" ),
nodefs = require( "fs" );
module.exports = function( _, anvil ) {
var startsWith = function( orig, compare ) {
return orig.slice( 0, compare.length ) === compare;
},
resolve_path = function( filepath ) {
// Check for path substitutions
if ( filepath.indexOf( "{{SOURCE}}" ) !== -1 ) {
filepath = filepath.replace( "{{SOURCE}}", anvil.config.working );
} else if ( filepath.indexOf( "{{OUTPUT}}" ) !== -1 ) {
filepath = filepath.replace( "{{OUTPUT}}", anvil.config.output );
}
// Resolve full path for comparison purposes
filepath = path.resolve( filepath );
// If path starts with the source directory, substitute in the working path
if ( startsWith( filepath, anvil.config.source ) ) {
filepath = filepath.replace( anvil.config.source, anvil.config.working );
}
// Convert path to relative paths for the Compass config file.
filepath = path.relative( anvil.project.root, filepath );
// Silly Windoze...
if ( path.sep === "\\" ) {
var regex = new RegExp( path.sep + path.sep, "gi" );
filepath = filepath.replace( regex, "/" );
}
return filepath;
},
rubify = function( value ) {
// Test for true, false, nil, and symbols
if ( /^(true|false|nil)$/i.test( value ) || /^:/.test( value ) ) {
return value;
} else {
// Encase in quotes
return '"' + value + '"';
}
};
return anvil.plugin( {
name: "anvil.compass",
activity: "pull",
dependencies: [ "anvil.workset" ],
// All possible Compass configuration options
config: {
"project_type": null,
"environment": null,
"project_path": null,
"http_path": null,
"css_dir": null,
"css_path": null,
"http_stylesheets_path": null,
"sass_dir": null,
"sass_path": null,
"images_dir": null,
"images_path": null,
"http_images_path": null,
"generated_images_dir": null,
"generated_images_path": null,
"http_generated_images_path": null,
"javascripts_dir": null,
"javascripts_path": null,
"http_javascripts_path": null,
"output_style": null,
"relative_assets": null,
"additional_import_paths": null,
"disable_warnings": null,
"sass_options": null,
"line_comments": null,
"preferred_syntax": null,
"fonts_dir": null,
"fonts_path": null,
"http_fonts_path": null,
"http_fonts_dir": null,
"sprite_engine": null,
"sprite_load_path": null
},
// Configuration keys that should not be written to compass.config.rb
config_exclude_keys: [
"config_file"
],
// The files from these directories will be excluded from anvil processing
// @TODO: Look at excluding by file extension
file_exclude_dirs: [
{ "folder": "sass_dir", "pattern": "\\.(sass|scss)$"}
],
cfg_file: anvil.config.working + "/compass.config.rb",
command_args: [],
output: [],
configure: function( config, command, done ) {
var self = this;
if( anvil.config[ "anvil.identify" ].continuous ) {
this.continuous = true;
this.command_args = [ "watch", "." ];
} else {
this.command_args = [ "compile", "." ];
}
if ( this.config.config_file ) {
// Read in configuration from file and create new file
anvil.fs.read( this.config.config_file, function( content, err ) {
if ( err ) {
anvil.events.raise( "build.stop", "Error reading Compass configuration file: ", err );
done();
} else {
self.parseConfig( content, function() {
self.writeConfigFile( content, true, done );
} );
}
} );
} else {
this.writeConfigFile( "", false, done );
}
},
parseConfig: function( content, callback ) {
// At this point we have the contents of a Ruby configuration file
var lines,
cfg_obj = {};
// Split file on line endings.
// @TODO: Test on Windows files
lines = content.split( /\n/ );
// Process lines into a JS configuration object
_.each(lines, function( line ) {
line = line.trim();
// Filter out empty lines and comments
if ( ! line || /^#/.test( line ) ) {
return false;
}
// Split variable assignments and extract strings
var split = line.split( /\s*=\s*/ ),
key = split[0],
val = split[1];
string_matches = val.match( /["|'](.*)["|']/ );
if ( string_matches ) {
val = string_matches[1];
}
// Remove trailing comments
var comment_index = val.indexOf( "#" );
if ( comment_index !== -1 ) {
val = val.slice( 0, comment_index );
}
val = val.trim();
if ( val ) {
cfg_obj[ key ] = val;
}
});
// Merge new object into plugin configuration
if ( ! _.isEmpty( cfg_obj ) ) {
this.config = _.extend( this.config, cfg_obj );
}
callback();
},
writeConfigFile: function( prepended_content, resolved_paths_only, callback ) {
prepended_content = prepended_content || "";
resolved_paths_only = resolved_paths_only || false;
var self = this,
cfg_lines = [],
cfg_contents = '',
// Configuration paths that need to be resolved
to_resolve = [
"sass_dir",
"css_dir",
"images_dir",
"javascripts_dir",
"fonts_dir",
"generated_images_dir",
"http_fonts_dir"
],
copyToFile = function(val, key) {
var copy = false;
if ( ! _.isNull( val ) && ! _.contains( self.config_exclude_keys, key ) ) {
if ( resolved_paths_only ) {
copy = _.contains( to_resolve, key );
} else {
copy = true;
}
}
return copy;
};
if ( prepended_content ) {
cfg_lines.push( prepended_content );
}
// Prepare config object for writing to Ruby file
_.each( self.config, function(val, key, list) {
if ( copyToFile( val, key ) ) {
self.config[key] = _.contains( to_resolve, key ) ? resolve_path( val ) : val;
cfg_lines.push(key + " = " + rubify( self.config[key] ) );
}
});
// Join file lines array into a string for writing to file
config_contents = cfg_lines.join("\r\n");
this.command_args.push( "--config=" + this.cfg_file );
// Figure out which directories we need to make anvil ignore
this.file_exclude_dirs = _.chain( this.file_exclude_dirs )
.reject( function( dir ) {
return _.isNull( self.config[ dir.folder ] );
} )
.map( function( dir ) {
dir.folder = path.resolve( self.config[ dir.folder ] );
return dir;
})
.value();
// Writing Compass configuration to file
anvil.fs.write( this.cfg_file, config_contents, function( err ) {
if ( err ) {
// Break build, couldn't create configuration file
anvil.raise( "log.error", err );
anvil.raise( "build.stop", "Error creating Compass configuration file" );
}
callback();
});
},
run: function( done, activity ) {
var self = this,
inExcludedDir = function( file ) {
// Reject if the file path is within the excluded directories
var reject = false;
_.each( self.file_exclude_dirs, function( dir ) {
if ( startsWith( file.workingPath, dir.folder ) ) {
var regex = new RegExp(dir.pattern);
if( regex.test( file.name ) ) {
file.noCopy = true;
}
}
});
},
onData = this.continuous ? this.onWatchData : this.onCompileData,
onExit = this.continuous ? this.onWatchExit : this.onCompileExit;
_.each( anvil.project.files, inExcludedDir );
if( this.continuous && this.compass ) {
this.runComplete = done;
return;
}
try {
// Execute Compass process
// Silly Windoze...
var compass = spawn( process.platform === "win32" ? "compass.bat" : "compass", self.command_args );
compass.stdout.on( "data", onData );
compass.stderr.on( "data", this.onError );
compass.on( "exit", onExit );
this.runComplete = done;
this.compass = compass;
this.timer = setTimeout( function() { done(); }, 1000 );
} catch ( error ) {
done( "", error );
}
},
onCompileData: function( data ) {
clearTimeout( this.timer );
this.output += data;
},
onError: function( data ) {
//i have no idea why but compass pumps a huge binary stream to std err
// for now, I am doing nothing with it b/c it's garbage.
console.log( data.toString() );
},
onWatchData: function( data ) {
clearTimeout( this.timer );
_.each( data.toString().split( '\n' ), function( line ) {
if( !_.isEmpty( line ) ) {
anvil.log.event( "Compass: " + line );
}
} );
if( this.runComplete ) {
this.runComplete();
this.runComplete = undefined;
}
},
onCompileExit: function( code ) {
var self = this;
self.compass = undefined;
anvil.log.event( "Compass Output" );
anvil.log.event( "----------------------" );
var lines = this.output.toString().split( /\n/ );
if ( lines ) {
_.each(lines, function( line ) {
if( !_.isEmpty( line ) ) {
anvil.log.event( line.trim() );
}
});
}
anvil.log.event( "----------------------" );
anvil.log.event( "Compass Compiling Complete" );
this.output = "";
this.runComplete();
},
onWatchExit: function( code ) {
var self = this;
self.compass = undefined;
}
} );
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment