forked from haithembelhaj/sass-inline-svg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
127 lines (105 loc) · 3.21 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// imports
const deasync = require('deasync');
const readFileSync = require('fs').readFileSync;
const resolve = require('path').resolve;
const types = require('node-sass').types;
const assign = require('object-assign');
const parse = require('htmlparser2').parseDOM;
const selectAll = require('css-select');
const selectOne = selectAll.selectOne;
const serialize = require('dom-serializer');
const svgToDataUri = require('mini-svg-data-uri');
const svgo = new (require('svgo'))();
const optimize = deasync(optimizeAsync);
const defaultOptions = { optimize: false, encodingFormat: 'base64' };
// exports
module.exports = inliner;
/**
* The SVG inliner function
* This is a factory that expects a base path abd returns the actual function
* @param base
* @param opts {optimize: true/false}
* @returns {Function}
*/
function inliner(base, opts) {
opts = assign({}, defaultOptions, opts);
return function(path, selectors) {
let content = readFileSync(resolve(base, path.getValue()));
if (selectors && selectors.getLength && selectors.getLength()) content = changeStyle(content, selectors);
if (opts.optimize) content = new Buffer(optimize(content).data);
return encode(content, { encodingFormat: opts.encodingFormat });
};
}
/**
* encode the string
* @param content
* @param opts
* @returns {types.String}
*/
function encode(content, opts) {
if (opts.encodingFormat === 'uri') {
return new types.String('url("' + svgToDataUri(content.toString('UTF-8')) + '")');
}
if (opts.encodingFormat === 'base64') {
return new types.String('url("data:image/svg+xml;base64,' + content.toString('base64') + '")');
}
throw new Error('encodingFormat ' + opts.encodingFormat + ' is not supported');
}
/**
* change the style of the svg
* @param source
* @param styles
* @returns {*}
*/
function changeStyle(source, selectors) {
const dom = parse(source, { xmlMode: true });
const svg = dom ? selectOne('svg', dom) : null;
selectors = mapToObj(selectors);
if (!svg) {
throw Error('Invalid svg file');
}
Object.keys(selectors).forEach(function(selector) {
const elements = selectAll(selector, svg);
let attribs = selectors[selector];
elements.forEach(function(element) {
assign(element.attribs, attribs);
});
});
return new Buffer(serialize(dom));
}
/**
* transform a sass map into a js object
* @param map
* @returns {null}
*/
function mapToObj(map) {
const obj = Object.create(null);
for (let i = 0, len = map.getLength(); i < len; i++) {
const key = map.getKey(i).getValue();
let value = map.getValue(i);
switch (value.toString()) {
case '[object SassMap]':
value = mapToObj(value);
break;
case '[object SassColor]':
if (value.getA() === 1) {
value = 'rgb(' + value.getR() + ',' + value.getG() + ',' + value.getB() + ')';
} else {
value = 'rgba(' + value.getR() + ',' + value.getG() + ',' + value.getB() + ',' + value.getA() + ')';
}
break;
default:
value = value.getValue();
}
obj[key] = value;
}
return obj;
}
function optimizeAsync(src, cb) {
svgo
.optimize(src)
.then(function(result) {
return cb(null, result);
})
.catch(cb);
}