glam
Version:
inline css for your jsx
242 lines (212 loc) • 6.61 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.cssLabels = cssLabels;
exports.default = parse;
var _flatten = require('./flatten');
var _flatten2 = _interopRequireDefault(_flatten);
var _hash = require('./hash');
var _hash2 = _interopRequireDefault(_hash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**** labels ****/
// toggle for debug labels.
// *shouldn't* have to mess with this manually
// import clean from './clean';
var hasLabels = process.env.NODE_ENV !== 'production';
function cssLabels(bool) {
hasLabels = !!bool;
}
var prefixedPseudoSelectors = {
'::placeholder': ['::-webkit-input-placeholder', '::-moz-placeholder', '::-ms-input-placeholder'],
':fullscreen': [':-webkit-full-screen', ':-moz-full-screen', ':-ms-fullscreen']
};
function isSelector(key) {
var possibles = [':', '.', '[', '>', ' '],
found = false,
ch = key.charAt(0);
for (var i = 0; i < possibles.length; i++) {
if (ch === possibles[i]) {
found = true;
break;
}
}
return found || key.indexOf('&') >= 0;
}
// from https://github.com/j2css/j2c/blob/5d381c2d721d04b54fabe6a165d587247c3087cb/src/helpers.js#L28-L61
// "Tokenizes" the selectors into parts relevant for the next function.
// Strings and comments are matched, but ignored afterwards.
// This is not a full tokenizers. It only recognizes comas, parentheses,
// strings and comments.
// regexp generated by scripts/regexps.js then trimmed by hand
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g;
/**
* This will split a coma-separated selector list into individual selectors,
* ignoring comas in strings, comments and in :pseudo-selectors(parameter, lists).
*
* @param {string} selector
* @return {string[]}
*/
function splitSelector(selector) {
if (selector.indexOf(',') === -1) {
return [selector];
}
var indices = [],
res = [],
inParen = 0,
o;
/*eslint-disable no-cond-assign*/
while (o = selectorTokenizer.exec(selector)) {
/*eslint-enable no-cond-assign*/
switch (o[0]) {
case '(':
inParen++;
break;
case ')':
inParen--;
break;
case ',':
if (inParen) break;
indices.push(o.index);
}
}
for (o = indices.length; o--;) {
res.unshift(selector.slice(indices[o] + 1));
selector = selector.slice(0, indices[o]);
}
res.unshift(selector);
return res;
}
function joinSelectors(a, b) {
var as = splitSelector(a).map(function (a) {
return !(a.indexOf('&') >= 0) ? '&' + a : a;
});
var bs = splitSelector(b).map(function (b) {
return !(b.indexOf('&') >= 0) ? '&' + b : b;
});
return bs.reduce(function (arr, b) {
return arr.concat(as.map(function (a) {
return b.replace(/\&/g, a);
}));
}, []).join(',');
}
function joinMediaQueries(a, b) {
return a ? '@media ' + a.substring(6) + ' and ' + b.substring(6) : b;
}
function isMediaQuery(key) {
return key.indexOf('@media') === 0;
}
function isSupports(key) {
return key.indexOf('@supports') === 0;
}
function joinSupports(a, b) {
return a ? '@supports ' + a.substring(9) + ' and ' + b.substring(9) : b;
}
// mutable! modifies dest.
function construct(dest, _ref) {
var _ref$selector = _ref.selector,
selector = _ref$selector === undefined ? '' : _ref$selector,
_ref$mq = _ref.mq,
mq = _ref$mq === undefined ? '' : _ref$mq,
_ref$supp = _ref.supp,
supp = _ref$supp === undefined ? '' : _ref$supp,
_ref$inputs = _ref.inputs,
inputs = _ref$inputs === undefined ? {} : _ref$inputs;
var inputArray = !Array.isArray(inputs) ? [inputs] : (0, _flatten2.default)(inputs);
inputArray.filter(function (x) {
return !!x;
}).forEach(function (input) {
var src = input;
Object.keys(src || {}).forEach(function (key) {
if (isSelector(key)) {
// todo - regex test the string to look for prefixedpseudos
if (prefixedPseudoSelectors[key]) {
prefixedPseudoSelectors[key].forEach(function (p) {
return construct(dest, {
selector: joinSelectors(selector, p),
mq: mq,
supp: supp,
inputs: src[key]
});
});
}
construct(dest, {
selector: joinSelectors(selector, key),
mq: mq,
supp: supp,
inputs: src[key]
});
} else if (isMediaQuery(key)) {
construct(dest, {
selector: selector,
mq: joinMediaQueries(mq, key),
supp: supp,
inputs: src[key]
});
} else if (isSupports(key)) {
construct(dest, {
selector: selector,
mq: mq,
supp: joinSupports(supp, key),
inputs: src[key]
});
} else {
var _dest = dest;
if (supp) {
_dest[supp] = _dest[supp] || {};
_dest = _dest[supp];
}
if (mq) {
_dest[mq] = _dest[mq] || {};
_dest = _dest[mq];
}
if (selector) {
_dest[selector] = _dest[selector] || {};
_dest = _dest[selector];
}
if (key === 'label') {
if (hasLabels) {
// concat at root of object
dest.label = dest.label.concat(src.label);
}
} else {
_dest[key] = src[key];
}
}
});
});
return dest;
}
function groupByType(style) {
// we can be sure it's not infinitely nested here
var plain = void 0,
selects = void 0,
medias = void 0,
supports = void 0;
Object.keys(style).forEach(function (key) {
if (key.indexOf('&') >= 0) {
selects = selects || {};
selects[key] = style[key];
} else if (key.indexOf('@media') === 0) {
medias = medias || {};
medias[key] = groupByType(style[key]);
} else if (key.indexOf('@supports') === 0) {
supports = supports || {};
supports[key] = groupByType(style[key]);
} else if (key === 'label') {
if (style.label.length > 0) {
plain = plain || {};
plain.label = hasLabels ? style.label.join('.') : '';
}
} else {
plain = plain || {};
plain[key] = style[key];
}
});
return { plain: plain, selects: selects, medias: medias, supports: supports };
}
function parse(css) {
var parsed = groupByType(construct({ label: [] }, { inputs: css }));
var className = 'css-' + (0, _hash2.default)(parsed);
return { className: className, parsed: parsed };
}