UNPKG

stylis-plugin-css-variables

Version:

Stylis plugin that transforms CSS variable into static values for non-supported browsers.

257 lines (201 loc) 8.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFallbackDeclaration = getFallbackDeclaration; exports.transformContent = transformContent; exports.memoizedTransformContent = void 0; var _memize = _interopRequireDefault(require("memize")); var _utils = require("./utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; 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"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } /** * Returns new string, removes characters from source string * @param {number} start Index at which to start changing the string. * @param {number} delCount An integer indicating the number of old chars to remove. * @param {string} newSubStr The String that is spliced in. * @return {string} A new string with the spliced substring. */ function splice(str, index, count, newSubStr) { if (newSubStr === void 0) { newSubStr = ''; } if (index > str.length - 1) return str; return str.substring(0, index) + newSubStr + str.substring(index + count); } /** * Interprets and retrieves the CSS property and value of a declaration rule. * @param {string} declaration A CSS declaration rule to parse. * @return {Array<string, ?string>} [prop, value] parsed from the declaration. */ function getPropValue(declaration) { // Start be separating (and preparing) the prop and value from the declaration. var _declaration$split = declaration.split(/:/), _declaration$split2 = _slicedToArray(_declaration$split, 2), prop = _declaration$split2[0], value = _declaration$split2[1]; prop = prop.trim(); // Creating map of string with var, parentheses and vars values. var regexp = new RegExp(/var|\(|\)/, 'g'); var map = { var: [], ob: [], cb: [], values: {} }; var matches = regexp.exec(value); while (matches != null) { switch (matches[0]) { case 'var': map.var.push(matches.index); break; case '(': map.ob.push(matches.index); break; case ')': default: map.cb.push(matches.index); break; } matches = regexp.exec(value); } if (map.var.length === 0 || map.cb.length !== map.ob.length) { return [prop, undefined]; } // filling character`s indices for removing var removeIndices = []; for (var i = 0; i < map.var.length; i++) { // calc var`s opened parentheses index var obIndex = map.ob.indexOf(map.var[i] + 3); // calc var`s closed parentheses index var cbIndex = 0; var ob = obIndex; while (map.ob[ob + 1] < map.cb[cbIndex] && ob < map.ob.length - 1) { ob++; cbIndex++; } while (map.ob[ob] > map.cb[cbIndex] && cbIndex < map.cb.length - 1) { cbIndex++; } // removes var and parentheses removeIndices.push(map.var[i]); removeIndices.push(map.var[i] + 1); removeIndices.push(map.var[i] + 2); removeIndices.push(map.ob[obIndex]); removeIndices.push(map.cb[cbIndex]); // removes var`s name var n = map.ob[obIndex] + 1; while (value[n - 1 || 0] !== ',' && n < map.cb[cbIndex]) { removeIndices.push(n); n++; } // gets var`s value from root var _value$substring$trim = value.substring(map.ob[obIndex] + 1, map.cb[cbIndex]).trim().split(','), _value$substring$trim2 = _slicedToArray(_value$substring$trim, 1), name = _value$substring$trim2[0]; var rootValue = (0, _utils.getRootPropertyValue)(name); // needs to fill value from root if (n === map.cb[cbIndex] || rootValue) { // removes var`s default value while (n < map.cb[cbIndex]) { removeIndices.push(n); n++; } map.values[map.var[i]] = rootValue; } // trim whitespaces while (value[n] === ' ') { removeIndices.push(n); n++; } } // applies map to string - removes var and insert values var count = 1; removeIndices.sort(function (a, b) { return b - a; }).forEach(function (sIndex, iIndex) { if (map.values[sIndex] != null) { value = splice(value, sIndex, count, map.values[sIndex]); count = 1; return; } if (iIndex === removeIndices.length - 1 || removeIndices[iIndex + 1] !== sIndex - 1) { value = splice(value, sIndex, count); count = 1; return; } count++; }); return [prop, value.trim()]; } /** * Interprets and retrieves the CSS fallback value of a declaration rule. * * @param {string} declaration A CSS declaration rule to parse. * @return {?string} A CSS declaration rule with a fallback (if applicable). */ function getFallbackDeclaration(declaration) { if (!(0, _utils.hasVariable)(declaration)) return undefined; var _getPropValue = getPropValue(declaration), _getPropValue2 = _slicedToArray(_getPropValue, 2), prop = _getPropValue2[0], value = _getPropValue2[1]; return value ? [prop, value].join(':') : undefined; } /** * Parses the incoming content from stylis to add fallback CSS values for * variables. * * @param {string} content Stylis content to parse. * @return {string} The transformed content with CSS variable fallbacks. */ function transformContent(content) { /* * Attempts to deconstruct the content to retrieve prop/value * CSS declaration pairs. * * Before: * 'background-color:var(--bg, black); font-size:14px;' * * After: * ['background-color:var(--bg, black)', ' font-size:14px'] */ var declarations = content.split(';').filter(Boolean); var didTransform = false; /* * With the declaration collection, we'll iterate over every declaration * to provide fallbacks (if applicable.) */ var parsed = declarations.reduce(function (styles, declaration) { // If no CSS variable is used, we return the declaration untouched. if (!(0, _utils.hasVariable)(declaration)) { return [].concat(styles, [declaration]); } // Retrieve the fallback a CSS variable is used in this declaration. var fallback = getFallbackDeclaration(declaration); /* * Prepend the fallback in our styles set. * * Before: * [ * ...styles, * 'background-color:var(--bg, black);' * ] * * After: * [ * ...styles, * 'background:black;', * 'background-color:var(--bg, black);' * ] */ if (fallback) { didTransform = true; return [].concat(styles, [fallback, declaration]); } return [].concat(styles, [declaration]); }, []); /* * We'll rejoin our declarations with a ; separator. * Note: We need to add a ; at the end for stylis to interpret correctly. */ var result = parsed.join(';').concat(';'); // We only want to return a value if we're able to locate a fallback value. return didTransform ? result : undefined; } var memoizedTransformContent = (0, _memize.default)(transformContent); exports.memoizedTransformContent = memoizedTransformContent;