UNPKG

glam

Version:

inline css for your jsx

141 lines (122 loc) 3.8 kB
// @flow // import type { AST } from './types'; import { createMarkupForStyles } from './CSSPropertyOperations'; const isBrowser = typeof window !== 'undefined'; import { prefixes, fallbacks, contentWrap } from './plugins'; // a flag to enable simulation meta tags on dom nodes // defaults to true in dev mode. recommend *not* to // toggle often. let canSimulate = process.env.NODE_ENV !== 'production'; // we use these flags for issuing warnings when simulate is called // in prod / in incorrect order let warned1 = false, warned2 = false; // toggles simulation activity. shouldn't be needed in most cases export function simulations(bool: boolean = true) { canSimulate = !!bool; } // takes a string, converts to lowercase, strips out nonalphanumeric. function simple(str: string, replace: string = '') { return str.toLowerCase().replace(/[^a-z0-9]/g, replace); } // 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: string): Array<string> { 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 selector(id: string, path?: string = '') { if (!id && path) { return path.replace(/\&/g, ''); } if (id && !path) return `.${id}`; let x = splitSelector(path) .map( x => x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.${id}`) // todo - make sure each sub selector has an & : `.${id}${x}`, ) .join(','); if (canSimulate && /^\&\:/.exec(path) && !/\s/.exec(path)) { x += `,.${id}[data-simulate-${simple(path)}]`; } return x; } function toCSS(node) { const result = prefixes(fallbacks(contentWrap(node))); return `${result.selector}{${createMarkupForStyles(result.style)}}`; } function toCSSArray(id: string, parsed: Object) { let css = []; // plugins here let { plain, selects, medias, supports } = parsed; // todo - :host? if (plain) { css.push(toCSS({ style: plain, selector: selector(id) })); } if (selects) { Object.keys(selects).forEach(key => css.push(toCSS({ style: selects[key], selector: selector(id, key) })), ); } if (medias) { Object.keys(medias).forEach(key => css.push(`${key}{${toCSSArray(id, medias[key]).join('')}}`), ); } if (supports) { Object.keys(supports).forEach(key => css.push(`${key}{${toCSSArray(id, supports[key]).join('')}}`), ); } return css; } // todo - if server side, then cache on classname export default function generate({ className, parsed, }: { className: string, parsed: Object, }) { return toCSSArray(className, parsed); }