Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ will generate
}
````

In svg files use no-sassvg class on elements where color, stroke or style should not be replaced:
````xml
<path stroke="#535A64" ... />
<path class="no-sassvg" stroke="#535A64" ... />
````
will generate
````xml
<path stroke="#{$strokecolor}" ... />
<path class="no-sassvg" stroke="#535A64" ... />
````


## Documentation
Documentation may be generated using sassdoc. Otherwise, just read the _sassvg.scss file, should be clear how to use the provided sassvg() and the sassvg-list() functions. Here are some screeenshots of the essential sassdoc parts:
Expand Down
198 changes: 98 additions & 100 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//require what we need
var through = require('through2')
, PluginError = require('plugin-error')
, fs = require('fs')
, cheerio = require('cheerio')
, SVGO = require('svgo')
, svgo = new SVGO();;
, PluginError = require('plugin-error')
, fs = require('fs')
, cheerio = require('cheerio')
, SVGO = require('svgo')
, svgo = new SVGO();;


// TODO rework with js classes/prototypes and promises, much less functions
Expand All @@ -15,150 +15,148 @@ const DATA_PREFIX = "data:image/svg+xml;charset=US-ASCII,";



function fileNameFromPath(filePath){
function fileNameFromPath(filePath) {
return filePath.split('\\').pop().split('/').pop().replace(/\.[^/.]+$/, "");
}

function folderNameFromPath(filePath){
function folderNameFromPath(filePath) {
var firstSplit = filePath.split('\\');
if(firstSplit.length > 1){
if (firstSplit.length > 1) {
return firstSplit[firstSplit.length - 2];
}

var secondSplit = filePath.split('/');
if(secondSplit.length > 1){
if (secondSplit.length > 1) {
return secondSplit[secondSplit.length - 2];
}
return "";
}

function sassVarRegex(variableName, optionalSuffix){
var suffix = (optionalSuffix !== undefined) ? encodeURIComponent(optionalSuffix) : "";
function sassVarRegex(variableName, optionalSuffix) {
var suffix = (optionalSuffix !== undefined) ? encodeURIComponent(optionalSuffix) : "";
return new RegExp(encodeURIComponent("#{$") + variableName + encodeURIComponent("}") + suffix, "gm"); // #{$variableName}
}



function addVariables(filePath, fileContent){
var $ = cheerio.load(fileContent, {
normalizeWhitespace: true,
xmlMode: true
});
if($('svg').length !== 1){
throw new PluginError(PLUGIN_NAME, "Wrong SVG-File at '" + filePath + "'.");
}
if($('[fill]').not('[fill=none]').length > 0){
$('[fill]').not('[fill=none]').not('.no-sassvg').attr('fill', '#{$fillcolor}');
}else{
$('svg').attr('fill', '#{$fillcolor}');
}
$('[style]').each(function(){
var fillValue = $(this).css("fill");
if(fillValue !== undefined && fillValue !== 'none'){
$(this).css("fill", "#{$fillcolor}");
}
var strokeValue = $(this).css("stroke");
if(strokeValue !== undefined && strokeValue !== 'none'){
$(this).css("stroke", "#{$strokecolor}");
}
});
$('svg').each(function(){
var styles = $(this).attr("style");
$(this).css("empty", "empty;#{$extrastyles}"); //not the very best solution, but makes it valid and works everytime - empty props will be regexed out again
});
$('[stroke]').not('[stroke=none]').attr('stroke', '#{$strokecolor}');
return $.html('svg'); //return only the svg
function addVariables(filePath, fileContent) {
var $ = cheerio.load(fileContent, {
normalizeWhitespace: true,
xmlMode: true
});
if ($('svg').length !== 1) {
throw new PluginError(PLUGIN_NAME, "Wrong SVG-File at '" + filePath + "'.");
}
if ($('[fill]').not('[fill=none]').length > 0) {
$('[fill]').not('[fill=none]').not('.no-sassvg').attr('fill', '#{$fillcolor}');
} else {
$('svg').attr('fill', '#{$fillcolor}');
}
$('[style]').not('.no-sassvg').each(function () {
var fillValue = $(this).css("fill");
if (fillValue !== undefined && fillValue !== 'none') {
$(this).css("fill", "#{$fillcolor}");
}
var strokeValue = $(this).css("stroke");
if (strokeValue !== undefined && strokeValue !== 'none') {
$(this).css("stroke", "#{$strokecolor}");
}
});
$('svg').each(function () {
var styles = $(this).attr("style");
$(this).css("empty", "empty;#{$extrastyles}"); //not the very best solution, but makes it valid and works everytime - empty props will be regexed out again
});
$('[stroke]').not('[stroke=none]').not('.no-sassvg').attr('stroke', '#{$strokecolor}');
return $.html('svg'); //return only the svg
}

function encodeSVG(dynamicContent){
function encodeSVG(dynamicContent) {
return encodeURIComponent(dynamicContent.replace(/[\t\n\r]/gmi, " ")) //replace tab, linefeed and carriage return
.replace(/\(/g, "%28") // opening brackets
.replace(/\)/g, "%29") // closing brackets
.replace(/["']/g, "%22"); // double quotes

}

function decodeVariables(encodedContent){
function decodeVariables(encodedContent) {
return encodedContent.replace(sassVarRegex("fillcolor"), "#{$fillcolor}")
.replace(sassVarRegex("strokecolor"), "#{$strokecolor}")
.replace(sassVarRegex("extrastyles", ";"), "#{$extrastyles}")
.replace(/empty%3A%20empty%3B/gm, "");//correct the empty styles props
.replace(/empty%3A%20empty%3B/gm, "");//correct the empty styles props
}

function assembleDataString(fileName, finalContent){
function assembleDataString(fileName, finalContent) {
return "@function sassvg-" + fileName + "($fillcolor, $strokecolor, $extrastyles){ @return '" + DATA_PREFIX + finalContent + "'; }\n";
}

function optimizeSvg(writeStream, cb, filePath, svgString){
svgo.optimize(svgString).then(function(result) {
var optimizedSvg;
if(result.error){
throw new PluginError(PLUGIN_NAME, "SVG couldn't be optimized: '" + file.path + "', will try to SASSVG it without optimizing.");
optimizedSvg = String(file.contents);
}else{
optimizedSvg = result.data;
}
sassvgIt(writeStream, cb, filePath, svgString);
function optimizeSvg(writeStream, cb, filePath, svgString) {
svgo.optimize(svgString).then(function (result) {
var optimizedSvg;
if (result.error) {
throw new PluginError(PLUGIN_NAME, "SVG couldn't be optimized: '" + file.path + "', will try to SASSVG it without optimizing.");
optimizedSvg = String(file.contents);
} else {
optimizedSvg = result.data;
}
sassvgIt(writeStream, cb, filePath, svgString);
});
}

function sassvgIt(writeStream, cb, filePath, svgString){
writeStream.write(
assembleDataString(
fileNameFromPath(filePath),
decodeVariables(
encodeSVG(
addVariables(
filePath,
svgString
)
)
)
)
);
cb();
function sassvgIt(writeStream, cb, filePath, svgString) {
writeStream.write(
assembleDataString(
fileNameFromPath(filePath),
decodeVariables(
encodeSVG(
addVariables(
filePath,
svgString
)
)
)
)
);
cb();
}

var options
var gulpSassvg = function(optionsGiven){
var options
var gulpSassvg = function (optionsGiven) {
options = optionsGiven || {};
options.tmpDir = optionsGiven.tmpDir || "./.tmp-sassvg/"; //TODO still necessary?
options.outputFolder = optionsGiven.outputFolder || "./scss/"; //TODO add some options
if( options.outputFolder.indexOf("/", options.outputFolder.length - 1) === -1 ){
options.outputFolder = options.outputFolder + "/";
}
options.outputMainFile = options.outputFolder + "_sassvg.scss";
options.outputDataFile = options.outputFolder + "_sassvg-data.scss";
options.optimizeSvg = (optionsGiven.optimizeSvg !== undefined) ? optionsGiven.optimizeSvg : true; //true = 25% less filesize, but 3 times as long to create the sass file
var writeStreamMain = fs.createWriteStream(options.outputMainFile);
writeStreamMain.write(fs.readFileSync(__dirname + "/_sassvg.scss", "utf8"));
writeStreamMain.end();
if (options.outputFolder.indexOf("/", options.outputFolder.length - 1) === -1) {
options.outputFolder = options.outputFolder + "/";
}
options.outputMainFile = options.outputFolder + "_sassvg.scss";
options.outputDataFile = options.outputFolder + "_sassvg-data.scss";
options.optimizeSvg = (optionsGiven.optimizeSvg !== undefined) ? optionsGiven.optimizeSvg : true; //true = 25% less filesize, but 3 times as long to create the sass file

var writeStreamMain = fs.createWriteStream(options.outputMainFile);
writeStreamMain.write(fs.readFileSync(__dirname + "/_sassvg.scss", "utf8"));
writeStreamMain.end();

var writeStream = fs.createWriteStream(options.outputDataFile);

var sassvgMap = "\n\n$sassvg-map: (";
function listStream(file, enc, cb){
var folderName = folderNameFromPath(file.path);
var fileName = fileNameFromPath(file.path);
sassvgMap += "'" + fileName + "': ('name': '" + fileName + "', 'folder': '" + folderName + "'),";
if(options.optimizeSvg){
optimizeSvg(writeStream, cb, file.path, String(file.contents))
}else{
sassvgIt(writeStream, cb, file.path, String(file.contents));
}

function listStream(file, enc, cb) {
var folderName = folderNameFromPath(file.path);
var fileName = fileNameFromPath(file.path);
sassvgMap += "'" + fileName + "': ('name': '" + fileName + "', 'folder': '" + folderName + "'),";
if (options.optimizeSvg) {
optimizeSvg(writeStream, cb, file.path, String(file.contents))
} else {
sassvgIt(writeStream, cb, file.path, String(file.contents));
}

}
function endStream(cb){
sassvgMap += ");";
writeStream.write(sassvgMap);

function endStream(cb) {
sassvgMap += ");";
writeStream.write(sassvgMap);
writeStream.end();
cb();
}
return through.obj(listStream, endStream);
}
module.exports = gulpSassvg;