UNPKG

@tdb/web

Version:

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

275 lines (247 loc) 9.06 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; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /* Based on Glamor's sheet https://github.com/threepointone/glamor/blob/667b480d31b3721a905021b26e1290ce92ca2879/src/sheet.js */ var isProd = process.env && process.env.NODE_ENV === 'production'; var isString = function isString(o) { return Object.prototype.toString.call(o) === '[object String]'; }; var StyleSheet = function () { function StyleSheet() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref$name = _ref.name, name = _ref$name === undefined ? 'stylesheet' : _ref$name, _ref$optimizeForSpeed = _ref.optimizeForSpeed, optimizeForSpeed = _ref$optimizeForSpeed === undefined ? isProd : _ref$optimizeForSpeed, _ref$isBrowser = _ref.isBrowser, isBrowser = _ref$isBrowser === undefined ? typeof window !== 'undefined' : _ref$isBrowser; _classCallCheck(this, StyleSheet); invariant(isString(name), '`name` must be a string'); this._name = name; this._deletedRulePlaceholder = '#' + name + '-deleted-rule____{}'; invariant(typeof optimizeForSpeed === 'boolean', '`optimizeForSpeed` must be a boolean'); this._optimizeForSpeed = optimizeForSpeed; this._isBrowser = isBrowser; this._serverSheet = undefined; this._tags = []; this._injected = false; this._rulesCount = 0; var node = this._isBrowser && document.querySelector('meta[property="csp-nonce"]'); this._nonce = node ? node.getAttribute('content') : null; } _createClass(StyleSheet, [{ key: 'setOptimizeForSpeed', value: function setOptimizeForSpeed(bool) { invariant(typeof bool === 'boolean', '`setOptimizeForSpeed` accepts a boolean'); invariant(this._rulesCount === 0, 'optimizeForSpeed cannot be when rules have already been inserted'); this.flush(); this._optimizeForSpeed = bool; this.inject(); } }, { key: 'isOptimizeForSpeed', value: function isOptimizeForSpeed() { return this._optimizeForSpeed; } }, { key: 'inject', value: function inject() { var _this = this; invariant(!this._injected, 'sheet already injected'); this._injected = true; if (this._isBrowser && this._optimizeForSpeed) { this._tags[0] = this.makeStyleTag(this._name); this._optimizeForSpeed = 'insertRule' in this.getSheet(); if (!this._optimizeForSpeed) { if (!isProd) { console.warn('StyleSheet: optimizeForSpeed mode not supported falling back to standard mode.'); // eslint-disable-line no-console } this.flush(); this._injected = true; } return; } this._serverSheet = { cssRules: [], insertRule: function insertRule(rule, index) { if (typeof index === 'number') { _this._serverSheet.cssRules[index] = { cssText: rule }; } else { _this._serverSheet.cssRules.push({ cssText: rule }); } return index; }, deleteRule: function deleteRule(index) { _this._serverSheet.cssRules[index] = null; } }; } }, { key: 'getSheetForTag', value: function getSheetForTag(tag) { if (tag.sheet) { return tag.sheet; } // this weirdness brought to you by firefox for (var i = 0; i < document.styleSheets.length; i++) { if (document.styleSheets[i].ownerNode === tag) { return document.styleSheets[i]; } } } }, { key: 'getSheet', value: function getSheet() { return this.getSheetForTag(this._tags[this._tags.length - 1]); } }, { key: 'insertRule', value: function insertRule(rule, index) { invariant(isString(rule), '`insertRule` accepts only strings'); if (!this._isBrowser) { if (typeof index !== 'number') { index = this._serverSheet.cssRules.length; } this._serverSheet.insertRule(rule, index); return this._rulesCount++; } if (this._optimizeForSpeed) { var sheet = this.getSheet(); if (typeof index !== 'number') { index = sheet.cssRules.length; } // this weirdness for perf, and chrome's weird bug // https://stackoverflow.com/questions/20007992/chrome-suddenly-stopped-accepting-insertrule try { sheet.insertRule(rule, index); } catch (err) { if (!isProd) { console.warn('StyleSheet: illegal rule: \n\n' + rule + '\n\nSee https://stackoverflow.com/q/20007992 for more info'); // eslint-disable-line no-console } return -1; } } else { var insertionPoint = this._tags[index]; this._tags.push(this.makeStyleTag(this._name, rule, insertionPoint)); } return this._rulesCount++; } }, { key: 'replaceRule', value: function replaceRule(index, rule) { if (this._optimizeForSpeed || !this._isBrowser) { var sheet = this._isBrowser ? this.getSheet() : this._serverSheet; if (!rule.trim()) { rule = this._deletedRulePlaceholder; } if (!sheet.cssRules[index]) { // @TBD Should we throw an error? return index; } sheet.deleteRule(index); try { sheet.insertRule(rule, index); } catch (err) { if (!isProd) { console.warn('StyleSheet: illegal rule: \n\n' + rule + '\n\nSee https://stackoverflow.com/q/20007992 for more info'); // eslint-disable-line no-console } // In order to preserve the indices we insert a deleteRulePlaceholder sheet.insertRule(this._deletedRulePlaceholder, index); } } else { var tag = this._tags[index]; invariant(tag, 'old rule at index `' + index + '` not found'); tag.textContent = rule; } return index; } }, { key: 'deleteRule', value: function deleteRule(index) { if (!this._isBrowser) { this._serverSheet.deleteRule(index); return; } if (this._optimizeForSpeed) { this.replaceRule(index, ''); } else { var tag = this._tags[index]; invariant(tag, 'rule at index `' + index + '` not found'); tag.parentNode.removeChild(tag); this._tags[index] = null; } } }, { key: 'flush', value: function flush() { this._injected = false; this._rulesCount = 0; if (this._isBrowser) { this._tags.forEach(function (tag) { return tag && tag.parentNode.removeChild(tag); }); this._tags = []; } else { // simpler on server this._serverSheet.cssRules = []; } } }, { key: 'cssRules', value: function cssRules() { var _this2 = this; if (!this._isBrowser) { return this._serverSheet.cssRules; } return this._tags.reduce(function (rules, tag) { if (tag) { rules = rules.concat(_this2.getSheetForTag(tag).cssRules.map(function (rule) { return rule.cssText === _this2._deletedRulePlaceholder ? null : rule; })); } else { rules.push(null); } return rules; }, []); } }, { key: 'makeStyleTag', value: function makeStyleTag(name, cssString, relativeToTag) { if (cssString) { invariant(isString(cssString), 'makeStyleTag acceps only strings as second parameter'); } var tag = document.createElement('style'); if (this._nonce) tag.setAttribute('nonce', this._nonce); tag.type = 'text/css'; tag.setAttribute('data-' + name, ''); if (cssString) { tag.appendChild(document.createTextNode(cssString)); } var head = document.head || document.getElementsByTagName('head')[0]; if (relativeToTag) { head.insertBefore(tag, relativeToTag); } else { head.appendChild(tag); } return tag; } }, { key: 'length', get: function get() { return this._rulesCount; } }]); return StyleSheet; }(); exports.default = StyleSheet; function invariant(condition, message) { if (!condition) { throw new Error('StyleSheet: ' + message + '.'); } }