195 lines
3.5 KiB
JavaScript
195 lines
3.5 KiB
JavaScript
|
'use strict';
|
||
|
var fs = require('fs');
|
||
|
var path = require('path');
|
||
|
var css = require('css');
|
||
|
var globby = require('globby');
|
||
|
var parse = require('parse-import');
|
||
|
var urlRegex = require('url-regex');
|
||
|
|
||
|
/**
|
||
|
* Get options
|
||
|
*
|
||
|
* @param {Array} rules
|
||
|
* @param {Object} opts
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function getOptions(rules, opts) {
|
||
|
var dir;
|
||
|
var obj = {
|
||
|
source: opts.source,
|
||
|
transform: opts.transform || function (val) {
|
||
|
return val;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
obj.path = opts.path || [];
|
||
|
obj.path = Array.isArray(obj.path) ? obj.path : [obj.path];
|
||
|
|
||
|
if (rules.length && rules[0].position && rules[0].position.source) {
|
||
|
dir = path.dirname(rules[0].position.source);
|
||
|
}
|
||
|
|
||
|
if (dir && obj.path.indexOf(dir) === -1) {
|
||
|
obj.path.unshift(dir);
|
||
|
}
|
||
|
|
||
|
if (!obj.path.length) {
|
||
|
obj.path.push(process.cwd());
|
||
|
}
|
||
|
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create error
|
||
|
*
|
||
|
* @param {String} file
|
||
|
* @param {String} src
|
||
|
* @param {Array} paths
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function createError(file, src, paths) {
|
||
|
var err = ['Failed to find ' + file];
|
||
|
|
||
|
if (src) {
|
||
|
err.push('from ' + src);
|
||
|
}
|
||
|
|
||
|
err.push([
|
||
|
'in [',
|
||
|
' ' + paths.join(',\n '),
|
||
|
']'
|
||
|
].join('\n'));
|
||
|
|
||
|
return err.join(' ');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create bad import rule error
|
||
|
*
|
||
|
* @param {String} rule
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function createImportError(rule) {
|
||
|
var url = rule.import ? rule.import.replace(/\r?\n/g, '\\n') : '<no url>';
|
||
|
var err = ['Bad import url: @import ' + url];
|
||
|
|
||
|
if (rule.position) {
|
||
|
err.push(' starting at line ' + rule.position.start.line + ' column ' + rule.position.start.column);
|
||
|
err.push(' ending at line ' + rule.position.end.line + ' column ' + rule.position.end.column);
|
||
|
|
||
|
if (rule.position.source) {
|
||
|
err.push(' in ' + rule.position.source);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return err.join('\n');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a file exists
|
||
|
*
|
||
|
* @param {String} file
|
||
|
* @param {String} src
|
||
|
* @param {Object} opts
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function exists(file, src, opts) {
|
||
|
var files = opts.path.map(function (dir) {
|
||
|
return path.join(dir, file);
|
||
|
});
|
||
|
|
||
|
files = globby.sync(files);
|
||
|
|
||
|
if (!files.length) {
|
||
|
throw new Error(createError(file, src, opts.path));
|
||
|
}
|
||
|
|
||
|
return files[0];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read the contents of a file
|
||
|
*
|
||
|
* @param {String} file
|
||
|
* @param {Object} opts
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function read(file, opts) {
|
||
|
var encoding = opts.encoding || 'utf8';
|
||
|
var data = opts.transform(fs.readFileSync(file, encoding));
|
||
|
return css.parse(data, {source: file}).stylesheet;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Run
|
||
|
*
|
||
|
* @param {Object} style
|
||
|
* @param {Object} opts
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function run(style, opts) {
|
||
|
opts = getOptions(style.rules, opts || {});
|
||
|
|
||
|
var rules = style.rules || [];
|
||
|
var ret = [];
|
||
|
|
||
|
rules.forEach(function (rule) {
|
||
|
if (rule.type !== 'import') {
|
||
|
ret.push(rule);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var importRule = '@import ' + rule.import + ';';
|
||
|
var data = parse(importRule)[0];
|
||
|
var pos = rule.position ? rule.position.source : null;
|
||
|
|
||
|
if (!data) {
|
||
|
throw Error(createImportError(rule));
|
||
|
}
|
||
|
|
||
|
if (urlRegex({ exact: true }).test(data.path)) {
|
||
|
ret.push(rule);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
opts.source = exists(data.path, pos, opts);
|
||
|
|
||
|
if (opts.path.indexOf(path.dirname(opts.source)) === -1) {
|
||
|
opts.path.unshift(path.dirname(opts.source));
|
||
|
}
|
||
|
|
||
|
var content = read(opts.source, opts);
|
||
|
run(content, opts);
|
||
|
|
||
|
if (!data.condition || !data.condition.length) {
|
||
|
ret = ret.concat(content.rules);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ret.push({
|
||
|
media: data.condition,
|
||
|
rules: content.rules,
|
||
|
type: 'media'
|
||
|
});
|
||
|
});
|
||
|
|
||
|
style.rules = ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Module exports
|
||
|
*/
|
||
|
|
||
|
module.exports = function (opts) {
|
||
|
return function (style) {
|
||
|
run(style, opts);
|
||
|
};
|
||
|
};
|