UNPKG

sass-convert

Version:
404 lines (310 loc) 12.7 kB
'use strict'; // istanbul ignore next var _createDecoratedClass = (function () { function defineProperties(target, descriptors, initializers) { for (var i = 0; i < descriptors.length; i++) { var descriptor = descriptors[i]; var decorators = descriptor.decorators; var key = descriptor.key; delete descriptor.key; delete descriptor.decorators; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor || descriptor.initializer) descriptor.writable = true; if (decorators) { for (var f = 0; f < decorators.length; f++) { var decorator = decorators[f]; if (typeof decorator === 'function') { descriptor = decorator(target, key, descriptor) || descriptor; } else { throw new TypeError('The decorator for method ' + descriptor.key + ' is of the invalid type ' + typeof decorator); } } if (descriptor.initializer !== undefined) { initializers[key] = descriptor; continue; } } Object.defineProperty(target, key, descriptor); } } return function (Constructor, protoProps, staticProps, protoInitializers, staticInitializers) { if (protoProps) defineProperties(Constructor.prototype, protoProps, protoInitializers); if (staticProps) defineProperties(Constructor, staticProps, staticInitializers); return Constructor; }; })(); // istanbul ignore next var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); // istanbul ignore next var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; // istanbul ignore next function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } // istanbul ignore next function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } // istanbul ignore next function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _child_process = require('child_process'); var _semver = require('semver'); var _semver2 = _interopRequireDefault(_semver); var _semverRegex = require('semver-regex'); var _semverRegex2 = _interopRequireDefault(_semverRegex); var _dargs = require('dargs'); var _dargs2 = _interopRequireDefault(_dargs); var _through2 = require('through2'); var _through22 = _interopRequireDefault(_through2); var _concatStream = require('concat-stream'); var _concatStream2 = _interopRequireDefault(_concatStream); var _which = require('which'); var _which2 = _interopRequireDefault(_which); var _objectAssign = require('object-assign'); var _objectAssign2 = _interopRequireDefault(_objectAssign); var _endsWith = require('ends-with'); var _endsWith2 = _interopRequireDefault(_endsWith); var _memoizeDecorator = require('memoize-decorator'); var _memoizeDecorator2 = _interopRequireDefault(_memoizeDecorator); var _es6Promise = require('es6-promise'); var _es6Denodeify = require('es6-denodeify'); var _es6Denodeify2 = _interopRequireDefault(_es6Denodeify); var promiseify = (0, _es6Denodeify2['default'])(_es6Promise.Promise); var whichp = promiseify(_which2['default']); var BIN = 'sass-convert'; var MINVERS = '>=3.4.5'; /** * Execute command in a child process. * * @see {@link http://nodejs.org/api/child_process.html} * @param {String} command * @param {Array} args * @return {Promise} */ function execp(command) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var childProcess = undefined; return new _es6Promise.Promise(function (resolve, reject) { args.push(function (err, stdout, stderr) { if (err) { (0, _objectAssign2['default'])(err, { message: err.message + ' `' + command + '` exited with error code ' + err.code, stdout: stdout, stderr: stderr }); reject(err); } else { resolve({ childProcess: childProcess, stdout: stdout, stderr: stderr }); } }); childProcess = _child_process.exec.apply(undefined, [command].concat(args)); }); } /** * Custom error for binary check. * * @param {String} message */ var BinaryError = (function (_Error) { _inherits(BinaryError, _Error); function BinaryError(message) { _classCallCheck(this, BinaryError); _get(Object.getPrototypeOf(BinaryError.prototype), 'constructor', this).call(this, message); // http://bit.ly/1yMzARU this.message = message || 'Could not find any executable for "' + BIN + '". Operation Aborted.'; } /** * Custom error for version check. * * @param {String} message */ _createClass(BinaryError, [{ key: 'name', get: function get() { return 'BinaryError'; } }]); return BinaryError; })(Error); var VersionError = (function (_Error2) { _inherits(VersionError, _Error2); function VersionError(message) { _classCallCheck(this, VersionError); _get(Object.getPrototypeOf(VersionError.prototype), 'constructor', this).call(this, message); // http://bit.ly/1yMzARU this.message = message || 'Invalid "' + BIN + '" version, must be ' + MINVERS; } /** * Check whether passed binary (Gem) is in $PATH, * and check its version. * * @param {String} bin * @return {Promise} */ _createClass(VersionError, [{ key: 'name', get: function get() { return 'VersionError'; } }]); return VersionError; })(Error); function checkBinary(bin) { /** * Check for binary min-version. * * @param {String} str * @return {Boolean} */ function checkVersion(str) { var version = str.match((0, _semverRegex2['default'])())[0]; return _semver2['default'].satisfies(version, MINVERS); } /** * Check for global binary and version. * * @return {Promise} */ function checkGlobal() { return whichp(bin).then(function () { return execp(bin + ' -v'); }, function () { throw new BinaryError(); }).then(function (res) { if (!checkVersion(res.stdout)) { throw new VersionError(); } }); } /** * Check for bundled binary and version. * * @return {Promise} */ function checkBundle() { return whichp('bundle').then(function () { return execp('bundle exec ' + bin + ' -v'); }).then(function (res) { if (!checkVersion(res.stdout)) { throw new VersionError(); } return { bundler: true }; }, function (err) { throw new BinaryError(); }); } return checkBundle()['catch'](checkGlobal); } /** * Run binary checks only once. * * @return {Promise} */ function checkBinaryCache() { if (!('promise' in checkBinaryCache)) { checkBinaryCache.promise = checkBinary(BIN); } return checkBinaryCache.promise; } var Converter = (function () { function Converter() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, Converter); this.options = options; this.bundler = false; } /** * Format the `sass-convert` command string. * * @return {Object} */ _createDecoratedClass(Converter, [{ key: 'isTransformable', /** * Returns whether file is eligible for convertion. * * @param {Object} file - Vinyl file Object * @return {Boolean} */ value: function isTransformable(file) { var ext = _path2['default'].extname(file.path); return (file.isBuffer() || file.isStream()) && (0, _endsWith2['default'])(ext, this.options.from); } /** * Convert file Buffer through `sass-convert` binary. * * @param {Buffer} file - Vinyl file * @param {String} enc - encoding * @return {Promise} */ }, { key: 'convertFile', value: function convertFile(file, enc) { var _command = this.command; var cmd = _command.cmd; var args = _command.args; var child = (0, _child_process.spawn)(cmd, args); var result = {}; file.pipe(child.stdin); child.stdout.pipe((0, _concatStream2['default'])(function (data) { result.stdout = data; })); child.stderr.pipe((0, _concatStream2['default'])(function (data) { result.stderr = data; })); return new _es6Promise.Promise(function (resolve, reject) { child.on('error', reject); child.on('close', function (code, signal) { if (code !== 0) { var message = '`' + cmd + ' ' + args.join(' ') + '` failed with code ' + code; var stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString() : ''; reject({ code: code, message: message, stderr: stderr }); } else { resolve(result); } }); }); } /** * Change file extension to `options.from`. * * @param {Object} file - Vinyl file */ }, { key: 'rename', value: function rename(file) { if (!this.options.rename) { return; } var ext = _path2['default'].extname(file.path); file.path = file.path.replace(ext, '.' + this.options.to); } /** * Returns a transform stream meant to be piped to a stream * of Sass, SCSS or CSS files. Apply convertion if matches. * * @return {Object} - Stream */ }, { key: 'stream', value: function stream() { var self = this; return _through22['default'].obj(function (file, enc, cb) { var stream = this; if (!self.isTransformable(file)) { // Pass-through. return cb(null, file); } // Matches `from`, let's convert it. checkBinaryCache().then(function (res) { self.bundler = res ? res.bundler : false; return self.convertFile(file, enc); }).then(function (result) { file.contents = new Buffer(result.stdout); self.rename(file); cb(null, file); }, function (err) { stream.emit('error', err); if (self.options.force) { // Continue stream chain, // but don't pass unconverted chunks. return cb(); } // Stop stream chain. stream.destroy(); stream.emit('end'); }); }); } }, { key: 'command', decorators: [_memoizeDecorator2['default']], get: function get() { var cmd = (this.bundler ? 'bundle exec ' : '') + 'sass-convert'; // Add required args. var options = (0, _objectAssign2['default'])({}, this.options, { 'stdin': true, 'no-cache': true }); // Filter unwanted or erroneous args. var includes = ['from', 'to', 'dasherize', 'indent', 'old', 'default-encoding', 'unix-newlines', 'stdin', 'no-cache']; var args = (0, _dargs2['default'])(options, { includes: includes }); return { cmd: cmd, args: args }; } }]); return Converter; })(); module.exports = function (options) { return new Converter(options).stream(); }; // Expose custom error constructors. module.exports.BinaryError = BinaryError; module.exports.VersionError = VersionError;