@tdb/web
Version:
Common condiguration for serving a web-site and testing web-based UI components.
264 lines (219 loc) • 8.34 kB
JavaScript
'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 + '.');
}
}