UNPKG

@tdb/web

Version:

Common condiguration for serving a web-site and testing web-based UI components.

264 lines (219 loc) 8.34 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _stringHash = require('string-hash'); var _stringHash2 = _interopRequireDefault(_stringHash); var _stylesheet = require('./lib/stylesheet'); var _stylesheet2 = _interopRequireDefault(_stylesheet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var sanitize = function sanitize(rule) { return rule.replace(/\/style/gi, '\\/style'); }; var StyleSheetRegistry = function () { function StyleSheetRegistry() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$styleSheet = _ref.styleSheet, styleSheet = _ref$styleSheet === undefined ? null : _ref$styleSheet, _ref$optimizeForSpeed = _ref.optimizeForSpeed, optimizeForSpeed = _ref$optimizeForSpeed === undefined ? false : _ref$optimizeForSpeed, _ref$isBrowser = _ref.isBrowser, isBrowser = _ref$isBrowser === undefined ? typeof window !== 'undefined' : _ref$isBrowser; _classCallCheck(this, StyleSheetRegistry); this._sheet = styleSheet || new _stylesheet2.default({ name: 'styled-jsx', optimizeForSpeed: optimizeForSpeed }); this._sheet.inject(); if (styleSheet && typeof optimizeForSpeed === 'boolean') { this._sheet.setOptimizeForSpeed(optimizeForSpeed); this._optimizeForSpeed = this._sheet.isOptimizeForSpeed(); } this._isBrowser = isBrowser; this._fromServer = undefined; this._indices = {}; this._instancesCounts = {}; this.computeId = this.createComputeId(); this.computeSelector = this.createComputeSelector(); } _createClass(StyleSheetRegistry, [{ key: 'add', value: function add(props) { var _this = this; if (undefined === this._optimizeForSpeed) { this._optimizeForSpeed = Array.isArray(props.css); this._sheet.setOptimizeForSpeed(this._optimizeForSpeed); this._optimizeForSpeed = this._sheet.isOptimizeForSpeed(); } if (this._isBrowser && !this._fromServer) { this._fromServer = this.selectFromServer(); this._instancesCounts = Object.keys(this._fromServer).reduce(function (acc, tagName) { acc[tagName] = 0; return acc; }, {}); } var _getIdAndRules = this.getIdAndRules(props), styleId = _getIdAndRules.styleId, rules = _getIdAndRules.rules; // Deduping: just increase the instances count. if (styleId in this._instancesCounts) { this._instancesCounts[styleId] += 1; return; } var indices = rules.map(function (rule) { return _this._sheet.insertRule(rule); }) // Filter out invalid rules .filter(function (index) { return index !== -1; }); this._indices[styleId] = indices; this._instancesCounts[styleId] = 1; } }, { key: 'remove', value: function remove(props) { var _this2 = this; var _getIdAndRules2 = this.getIdAndRules(props), styleId = _getIdAndRules2.styleId; invariant(styleId in this._instancesCounts, 'styleId: `' + styleId + '` not found'); this._instancesCounts[styleId] -= 1; if (this._instancesCounts[styleId] < 1) { var tagFromServer = this._fromServer && this._fromServer[styleId]; if (tagFromServer) { tagFromServer.parentNode.removeChild(tagFromServer); delete this._fromServer[styleId]; } else { this._indices[styleId].forEach(function (index) { return _this2._sheet.deleteRule(index); }); delete this._indices[styleId]; } delete this._instancesCounts[styleId]; } } }, { key: 'update', value: function update(props, nextProps) { this.add(nextProps); this.remove(props); } }, { key: 'flush', value: function flush() { this._sheet.flush(); this._sheet.inject(); this._fromServer = undefined; this._indices = {}; this._instancesCounts = {}; this.computeId = this.createComputeId(); this.computeSelector = this.createComputeSelector(); } }, { key: 'cssRules', value: function cssRules() { var _this3 = this; var fromServer = this._fromServer ? Object.keys(this._fromServer).map(function (styleId) { return [styleId, _this3._fromServer[styleId]]; }) : []; var cssRules = this._sheet.cssRules(); return fromServer.concat(Object.keys(this._indices).map(function (styleId) { return [styleId, _this3._indices[styleId].map(function (index) { return cssRules[index].cssText; }).join('\n')]; }) // filter out empty rules .filter(function (rule) { return Boolean(rule[1]); })); } /** * createComputeId * * Creates a function to compute and memoize a jsx id from a basedId and optionally props. */ }, { key: 'createComputeId', value: function createComputeId() { var cache = {}; return function (baseId, props) { if (!props) { return 'jsx-' + baseId; } var propsToString = String(props); var key = baseId + propsToString; // return `jsx-${hashString(`${baseId}-${propsToString}`)}` if (!cache[key]) { cache[key] = 'jsx-' + (0, _stringHash2.default)(baseId + '-' + propsToString); } return cache[key]; }; } /** * createComputeSelector * * Creates a function to compute and memoize dynamic selectors. */ }, { key: 'createComputeSelector', value: function createComputeSelector() { var selectoPlaceholderRegexp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : /__jsx-style-dynamic-selector/g; var cache = {}; return function (id, css) { // Sanitize SSR-ed CSS. // Client side code doesn't need to be sanitized since we use // document.createTextNode (dev) and the CSSOM api sheet.insertRule (prod). if (!this._isBrowser) { css = sanitize(css); } var idcss = id + css; if (!cache[idcss]) { cache[idcss] = css.replace(selectoPlaceholderRegexp, id); } return cache[idcss]; }; } }, { key: 'getIdAndRules', value: function getIdAndRules(props) { var _this4 = this; if (props.dynamic) { var styleId = this.computeId(props.styleId, props.dynamic); return { styleId: styleId, rules: Array.isArray(props.css) ? props.css.map(function (rule) { return _this4.computeSelector(styleId, rule); }) : [this.computeSelector(styleId, props.css)] }; } return { styleId: this.computeId(props.styleId), rules: Array.isArray(props.css) ? props.css : [props.css] }; } /** * selectFromServer * * Collects style tags from the document with id __jsx-XXX */ }, { key: 'selectFromServer', value: function selectFromServer() { var elements = Array.prototype.slice.call(document.querySelectorAll('[id^="__jsx-"]')); return elements.reduce(function (acc, element) { var id = element.id.slice(2); acc[id] = element; return acc; }, {}); } }]); return StyleSheetRegistry; }(); exports.default = StyleSheetRegistry; function invariant(condition, message) { if (!condition) { throw new Error('StyleSheetRegistry: ' + message + '.'); } }