UNPKG

@solislab/postcss-type

Version:

PostCSS plugin to support responsive typography shorthands.

225 lines (189 loc) 6.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _postcss = require('postcss'); var postcss = _interopRequireWildcard(_postcss); var _lodash = require('lodash.curry'); var _lodash2 = _interopRequireDefault(_lodash); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } /** * PostCSS AtRule * @see {@link http://api.postcss.org/AtRule.html|postcss API > AtRule} * @typedef {Object} postcss.AtRule */ /** * PostCSS Declaration * @see {@link http://api.postcss.org/Declaration.html|postcss API > Declaration} * @typedef {Object} postcss.Declaration */ /** * PostCSS Node * @see {@link http://api.postcss.org/Node.html|postcss API > Node} * @typedef {Object} postcss.Node */ /** * Create an @media rule for a custom media query * @param {string} media * @return {postcss.AtRule} */ var createMediaRule = function createMediaRule(media) { return postcss.atRule({ name: 'media', params: '(' + media + ')' }); }; /** * Create a declaration for a pair of key and value. Doesn't do anything if * the value is empty or '/' * * @param {string} key * @param {string} value * @param {Node} source * @return {postcss.Declaration | undefined} */ var createDecl = function createDecl(key, value, source) { if (typeof value === 'undefined' || value === '/') { return undefined; } else { value = String(value); var decl = postcss.decl({ prop: key, value: value }); decl.source = source.source; return decl; } }; /** * Append declaration to an AtRule if declaration is defined. * This function is curried. * * @param {postcss.AtRule} atRule * @param {postcss.Declaration | undefined} decl * @return {postcss.AtRule} */ var appendDecl = (0, _lodash2.default)(function (atRule, decl) { return decl ? atRule.append(decl) : atRule; }); /** * Insert declaration into a parent rule before a certain position. * This function is curried. * * @param {postcss.Node} parent * @param {postcss.Node} position * @param {postcss.Declaration} decl * @return {postcss.Node} */ var insertDeclBefore = (0, _lodash2.default)(function (parent, position, decl) { return decl ? parent.insertBefore(position, decl) : parent; }); /** * Return true if the value's unit is px * * @param {string} val * @return {boolean} */ var isPx = function isPx(val) { return (/\dpx/.test(val) ); }; /** * Return true if the string is a custom media query * * @param {string} val * @return {boolean} */ var isCustomMedia = function isCustomMedia(val) { return (/^--/.test(val) ); }; /** * Format and round up a float number to a specific number of decimal places * * @param {number} num * @param {number} dec * @return {number} */ var toDecimalPlaces = function toDecimalPlaces(num, dec) { var multiplier = Math.pow(10, dec); return Math.round(num * multiplier) / multiplier; }; /** * Convert a pixel value to a ratio * * @param {string} val * @param {number} ref * @return {number} */ var pxToRatio = function pxToRatio(val, ref) { return toDecimalPlaces(parseFloat(val) / parseFloat(ref), 5); }; /** * Convert a pixel value to an em value * * @param {string} val * @param {string} ref * @return {string} */ var pxToEm = function pxToEm(val, ref) { return pxToRatio(val, ref) + 'em'; }; /** * Convert a pixel value to a rem value * * @param {string} val * @param {string} ref * @return {string} */ var pxToRem = function pxToRem(val, ref) { return pxToRatio(val, ref) + 'rem'; }; /** * PostCSS plugin to allow using `@type <media> <font-size> <line-height> <letter-spacing>` syntax */ exports.default = postcss.plugin('postcss-type', function () { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return function (root) { if (options.rootSize && !isPx(options.rootSize)) { throw root.error('rootSize option for postcss-type must be in pixel unit.'); } var createFromValue = function createFromValue(value, rule) { var frags = value.split(/\s+/); var media = frags.length && isCustomMedia(frags[0]) ? frags.shift() : undefined; var _frags = _slicedToArray(frags, 3), fontSize = _frags[0], lineHeight = _frags[1], letterSpacing = _frags[2]; if (!fontSize) { throw rule.error('Missing typography declarations for @type.'); } if (fontSize && fontSize !== '/') { if (typeof lineHeight !== 'undefined' && isPx(lineHeight)) { lineHeight = pxToRatio(lineHeight, fontSize); } if (typeof letterSpacing !== 'undefined' && isPx(letterSpacing)) { letterSpacing = pxToEm(letterSpacing, fontSize); } } if (options.rootSize && isPx(options.rootSize)) { fontSize = pxToRem(fontSize, options.rootSize); } var mediaRule = media ? createMediaRule(media) : undefined; var insertTypeDecls = media ? appendDecl(mediaRule) : insertDeclBefore(rule.parent, rule); insertTypeDecls(createDecl('font-size', fontSize, rule)); insertTypeDecls(createDecl('line-height', lineHeight, rule)); insertTypeDecls(createDecl('letter-spacing', letterSpacing, rule)); if (mediaRule) { rule.replaceWith(mediaRule); } else { rule.remove(); } }; root.walk(function (rule) { if (rule.type === 'atrule' && rule.name === 'type') { createFromValue(rule.params, rule); } if (rule.type === 'decl' && rule.prop === 'type') { createFromValue(rule.value, rule); } }); }; }); module.exports = exports['default'];