import * as css from 'css'; import React from 'react'; let newId = 0; const map = new WeakMap(); let sheet = null; // Ensure styles exist for a component. const ensureComponentStyles = (constructor, styles) => { if (!window || !styles || map.has(constructor)) { return; } newId += 1; const id = newId.toString(36); map.set(constructor, id); // Ensure sheet exists. if (!sheet) { sheet = window.document.createElement('style'); sheet.id = 'stylist-styles'; window.document.getElementsByTagName('head')[0].appendChild(sheet); } // All rules made component-specific. for (const style of styles) { const ast = css.parse(style); ast.stylesheet.rules = mapStyleRulesTree(ast.stylesheet.rules, id); sheet.innerHTML += css.stringify(ast); } } const mapStyleRulesTree = (rules, id) => rules.map((rule) => { // Recur for mediaqueries. if ('media' === rule.type) { rule.rules = mapStyleRulesTree(rule.rules, id); } if (!rule.selectors) { return rule; } return { ...rule, ...{ selectors: rule.selectors.map((rule) => `[data-component-style="${id}"] ${rule}` ), }, }; }); export default function contempo(styles) { return function (Component) { if (!Array.isArray(styles)) { styles = [styles]; } const createRenderFunction = (constructor, markupFn) => function() { ensureComponentStyles(constructor, styles); const markup = markupFn.apply(this, arguments); const id = map.get(constructor); if (!id) { return markup; } return {markup}; } if (Component.prototype.render) { class ContempoComponent extends Component { } ContempoComponent.prototype.render = createRenderFunction( Component, Component.prototype.render ); return ContempoComponent; } else { return createRenderFunction(Component, Component); } }}