'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') : ''; 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); }; };