gulp-cssimport
Version:
Parses a CSS file, finds imports, grabs the content of the linked file and replaces the import statement with it.
204 lines (191 loc) • 5.53 kB
JavaScript
;
var through = require("through2");
var path = require("path");
var deepExtend = require("deep-extend");
var fs = require("fs");
var pify = require("pify");
var gutil = require("gulp-util");
var collect = require("collect-stream");
var hh = require("http-https");
var minimatch = require("minimatch");
var applySourceMap = require("vinyl-sourcemaps-apply");
var MagicString = require("magic-string");
var PLUGIN_NAME = "gulp-cssimport";
var readFile = pify(fs.readFile);
var trim = require("lodash.trim");
var format = require("util").format;
var defaults = {
extensions: null,
filter: null,
matchPattern: null,
matchOptions: {
matchBase: true
},
limit: 5000
};
module.exports = function cssImport(options) {
options = deepExtend({}, defaults, options || {});
if (options.extensions && !Array.isArray(options.extensions)) {
options.extensions = options.extensions.toString().split(",").map(function (x) {
return x.trim();
});
}
var stream;
var cssCount = 0;
function fileContents(vinyl, encoding, callback) {
if (!stream) {
stream = this;
}
// https://github.com/kevva/import-regex/
var regex = '(?:@import)(?:\\s)(?:url)?(?:(?:(?:\\()(["\'])?(?:[^"\')]+)\\1(?:\\))|(["\'])(?:.+)\\2)(?:[A-Z\\s])*)+(?:;)'; // eslint-disable-line
var importRe = new RegExp(regex, "gi");
var match;
var file = [];
var lastpos = 0;
var promises = [];
var contents = vinyl.contents.toString();
while ((match = importRe.exec(contents)) !== null) {
var match2 = /@import\s+(?:url\()?(.+(?=['"\)]))(?:\))?.*/ig.exec(match[0]);
var importPath = trim(match2[1], "'\"");
if (!isMatch(importPath, options)) {
continue;
}
file[file.length] = contents.slice(lastpos, match.index);
var index = file.length;
file[index] = format("importing file %s from %s", importPath, vinyl.relative);
lastpos = importRe.lastIndex;
// Start resolving.
if (++cssCount > options.limit) {
stream.emit("error", new Error("Exceed limit. Recursive include?"));
return;
}
(function(index) {
var result = {index: index, importPath: importPath};
if (!isUrl(importPath)) {
var importFile = path.resolve(path.dirname(vinyl.path), importPath);
promises.push(readFile(importFile, "utf8").then(function(contents) {
result.importFile = importFile;
result.contents = contents;
return result;
}));
} else {
promises[promises.length] = new Promise(function(resolve, reject) {
var req = hh.request(importPath, function (res) {
collect(res, function (err, data) {
if (err) return reject(err);
result.contents = data.toString();
resolve(result);
});
});
req.on("error", reject);
req.end();
});
}
})(index);
}
// Nothing to import.
if (promises.length === 0) {
callback(null, vinyl);
return;
}
// Adding trailing piece.
file[file.length] = contents.slice(lastpos);
// Waiting promises.
Promise.all(promises).then(function(results) {
for (var i = 0; i < results.length; i++) {
var result = results[i];
var vfile = new gutil.File({
path: result.importFile,
contents: new Buffer(result.contents)
});
(function(result) {
results[i] = pify(fileContents)(vfile, null).then(function(vfile) {
result.contents = vfile.contents.toString();
return result;
});
})(result);
}
return Promise.all(results);
})
.then(function(results) {
var iterator = function() {};
if (vinyl.sourceMap) {
var bundle = new MagicString.Bundle();
iterator = function(file, result) {
bundle.addSource({
filename: result.importPath,
content: new MagicString(result.contents)
});
};
}
for (var i = 0; i < results.length; i++) {
var result = results[i];
var index = result.index;
var contents = result.contents;
file[index] = contents;
iterator(file, result);
}
vinyl.contents = new Buffer(file.join(""));
if (vinyl.sourceMap) {
var map = bundle.generateMap({
file: vinyl.relative,
includeContent: true,
hires: true
});
applySourceMap(vinyl, map);
}
callback(null, vinyl);
})
.catch(function(err) {
callback(new gutil.PluginError(PLUGIN_NAME, err));
});
}
return through.obj(fileContents);
};
function isMatch(path, options) {
if (!options) {
return true;
}
if (!path) {
return false;
}
options = options || {};
var result;
if (options.filter instanceof RegExp) {
var filter = options.filter;
filter.lastIndex = 0;
result = filter.test(path);
}
if (options.matchPattern && !isUrl(path)) {
var matchPattern = options.matchPattern;
result = minimatch(path, matchPattern, options.matchOptions);
}
if (options.extensions) {
var extensions = options.extensions;
var fileExt = getExtension(path);
for (var k = 0; k < extensions.length; k++) {
var extension = extensions[k];
var isInverse = extension.charAt(0) === "!";
if (isInverse) {
extension = extension.slice(1);
}
if (isInverse && fileExt === extension) { // !sass , sass === css
return false;
} else if (!isInverse && fileExt !== extension) {
return false;
}
}
}
if (typeof result === "undefined") {
result = true;
}
return result;
}
function isUrl(s) {
var regexp = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
return regexp.test(s);
}
function getExtension(p) {
p = String(p);
return p.substr(p.lastIndexOf(".") + 1);
}