-
Notifications
You must be signed in to change notification settings - Fork 54
/
css-preprocessor.js
107 lines (100 loc) · 3.65 KB
/
css-preprocessor.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
/**
* CssPreprocessor
* ===============
*/
var inherit = require('inherit');
var path = require('path');
var Vow = require('vow');
var vowFs = require('../fs/async-fs');
/**
* CssPreprocessor — класс для препроцессинга CSS. Заменяет import'ы на содержимое CSS-файлов,
* изменяет url'ы картинок.
* @name CssPreprocessor
*/
module.exports = inherit({
/**
* Конструктор.
*/
__constructor: function () {
this._buildCssRelativeUrl = function (url) {
return url;
};
},
/**
* Устанавливает функцию для построения относительных путей.
* @var {Function} builder
*/
setCssRelativeUrlBuilder: function (builder) {
this._buildCssRelativeUrl = builder;
},
/**
* Запускает препроцессинг.
* @param {String} data CSS для препроцессинга.
* @param {String} filename Имя результирующего файла.
*/
preprocess: function (data, filename) {
return this._processIncludes(this._processUrls(data, filename), filename);
},
/**
* @returns {Promise}
*/
preprocessIncludes: function (data, filename) {
return this._processIncludes(data, filename);
},
_processUrls: function (data, filename) {
var _this = this;
return data
.replace(/(?:@import\s*)?url\(('[^']+'|"[^"]+"|[^'")]+)\)/g, function (s, url) {
if (s.indexOf('@import') === 0) {
return s;
}
// Тип кавычки
var q = '';
var firstChar = url.charAt(0);
if (firstChar === '\'' || firstChar === '"') {
url = url.substr(1, url.length - 2);
q = firstChar;
}
return 'url(' + q + _this._resolveCssUrl(url, filename) + q + ')';
}).replace(/src=[']?([^',\)]+)[']?/g, function (s, url) {
return 'src=\'' + _this._resolveCssUrl(url, filename) + '\'';
});
},
_resolveCssUrl: function (url, filename) {
if (url.substr(0, 5) === 'data:' ||
url.substr(0, 2) === '//' ||
url.substr(0, 1) === '#' ||
~url.indexOf('http://') ||
~url.indexOf('https://')
) {
return url;
} else {
return this._buildCssRelativeUrl(url, filename);
}
},
_processIncludes: function (data, filename) {
var _this = this;
var filesToLoad = {};
var regex = /@import\s*(?:url\()?["']?([^"'\)]+)["']?(?:\))?\s*;/g;
var match;
var loadPromises = [];
function addLoadPromise (url) {
var includedFilename = path.resolve(path.dirname(filename), url);
loadPromises.push(vowFs.read(includedFilename, 'utf8').then(function (data) {
return _this.preprocess(data, includedFilename).then(function (preprocessedData) {
filesToLoad[url] = preprocessedData;
});
}));
}
while (!!(match = regex.exec(data))) {
addLoadPromise(match[1]);
}
return Vow.all(loadPromises).then(function () {
return data.replace(/@import\s*(?:url\()?["']?([^"'\)]+)["']?(?:\))?\s*;/g, function (s, url) {
var pre = '/* ' + url + ': begin */ /**/\n';
var post = '\n/* ' + url + ': end */ /**/\n';
return pre + ' ' + filesToLoad[url].replace(/\n/g, '\n ') + post;
});
});
}
});