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
JavaScript
;
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;