useStyleRegister.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  5. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  6. import _typeof from "@babel/runtime/helpers/esm/typeof";
  7. import hash from '@emotion/hash';
  8. import { removeCSS, updateCSS } from "rc-util/es/Dom/dynamicCSS";
  9. import * as React from 'react';
  10. // @ts-ignore
  11. import unitless from '@emotion/unitless';
  12. import { compile, serialize, stringify } from 'stylis';
  13. import { contentQuotesLinter, hashedAnimationLinter } from "../linters";
  14. import StyleContext, { ATTR_CACHE_PATH, ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE } from "../StyleContext";
  15. import { isClientSide, toStyleStr } from "../util";
  16. import { CSS_FILE_STYLE, existPath, getStyleAndHash } from "../util/cacheMapUtil";
  17. import useGlobalCache from "./useGlobalCache";
  18. var SKIP_CHECK = '_skip_check_';
  19. var MULTI_VALUE = '_multi_value_';
  20. // ============================================================================
  21. // == Parser ==
  22. // ============================================================================
  23. // Preprocessor style content to browser support one
  24. export function normalizeStyle(styleStr) {
  25. var serialized = serialize(compile(styleStr), stringify);
  26. return serialized.replace(/\{%%%\:[^;];}/g, ';');
  27. }
  28. function isCompoundCSSProperty(value) {
  29. return _typeof(value) === 'object' && value && (SKIP_CHECK in value || MULTI_VALUE in value);
  30. }
  31. // 注入 hash 值
  32. function injectSelectorHash(key, hashId, hashPriority) {
  33. if (!hashId) {
  34. return key;
  35. }
  36. var hashClassName = ".".concat(hashId);
  37. var hashSelector = hashPriority === 'low' ? ":where(".concat(hashClassName, ")") : hashClassName;
  38. // 注入 hashId
  39. var keys = key.split(',').map(function (k) {
  40. var _firstPath$match;
  41. var fullPath = k.trim().split(/\s+/);
  42. // 如果 Selector 第一个是 HTML Element,那我们就插到它的后面。反之,就插到最前面。
  43. var firstPath = fullPath[0] || '';
  44. var htmlElement = ((_firstPath$match = firstPath.match(/^\w+/)) === null || _firstPath$match === void 0 ? void 0 : _firstPath$match[0]) || '';
  45. firstPath = "".concat(htmlElement).concat(hashSelector).concat(firstPath.slice(htmlElement.length));
  46. return [firstPath].concat(_toConsumableArray(fullPath.slice(1))).join(' ');
  47. });
  48. return keys.join(',');
  49. }
  50. // Parse CSSObject to style content
  51. export var parseStyle = function parseStyle(interpolation) {
  52. var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  53. var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
  54. root: true,
  55. parentSelectors: []
  56. },
  57. root = _ref.root,
  58. injectHash = _ref.injectHash,
  59. parentSelectors = _ref.parentSelectors;
  60. var hashId = config.hashId,
  61. layer = config.layer,
  62. path = config.path,
  63. hashPriority = config.hashPriority,
  64. _config$transformers = config.transformers,
  65. transformers = _config$transformers === void 0 ? [] : _config$transformers,
  66. _config$linters = config.linters,
  67. linters = _config$linters === void 0 ? [] : _config$linters;
  68. var styleStr = '';
  69. var effectStyle = {};
  70. function parseKeyframes(keyframes) {
  71. var animationName = keyframes.getName(hashId);
  72. if (!effectStyle[animationName]) {
  73. var _parseStyle = parseStyle(keyframes.style, config, {
  74. root: false,
  75. parentSelectors: parentSelectors
  76. }),
  77. _parseStyle2 = _slicedToArray(_parseStyle, 1),
  78. _parsedStr = _parseStyle2[0];
  79. effectStyle[animationName] = "@keyframes ".concat(keyframes.getName(hashId)).concat(_parsedStr);
  80. }
  81. }
  82. function flattenList(list) {
  83. var fullList = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  84. list.forEach(function (item) {
  85. if (Array.isArray(item)) {
  86. flattenList(item, fullList);
  87. } else if (item) {
  88. fullList.push(item);
  89. }
  90. });
  91. return fullList;
  92. }
  93. var flattenStyleList = flattenList(Array.isArray(interpolation) ? interpolation : [interpolation]);
  94. flattenStyleList.forEach(function (originStyle) {
  95. // Only root level can use raw string
  96. var style = typeof originStyle === 'string' && !root ? {} : originStyle;
  97. if (typeof style === 'string') {
  98. styleStr += "".concat(style, "\n");
  99. } else if (style._keyframe) {
  100. // Keyframe
  101. parseKeyframes(style);
  102. } else {
  103. var mergedStyle = transformers.reduce(function (prev, trans) {
  104. var _trans$visit;
  105. return (trans === null || trans === void 0 || (_trans$visit = trans.visit) === null || _trans$visit === void 0 ? void 0 : _trans$visit.call(trans, prev)) || prev;
  106. }, style);
  107. // Normal CSSObject
  108. Object.keys(mergedStyle).forEach(function (key) {
  109. var value = mergedStyle[key];
  110. if (_typeof(value) === 'object' && value && (key !== 'animationName' || !value._keyframe) && !isCompoundCSSProperty(value)) {
  111. var subInjectHash = false;
  112. // 当成嵌套对象来处理
  113. var mergedKey = key.trim();
  114. // Whether treat child as root. In most case it is false.
  115. var nextRoot = false;
  116. // 拆分多个选择器
  117. if ((root || injectHash) && hashId) {
  118. if (mergedKey.startsWith('@')) {
  119. // 略过媒体查询,交给子节点继续插入 hashId
  120. subInjectHash = true;
  121. } else if (mergedKey === '&') {
  122. // 抹掉 root selector 上的单个 &
  123. mergedKey = injectSelectorHash('', hashId, hashPriority);
  124. } else {
  125. // 注入 hashId
  126. mergedKey = injectSelectorHash(key, hashId, hashPriority);
  127. }
  128. } else if (root && !hashId && (mergedKey === '&' || mergedKey === '')) {
  129. // In case of `{ '&': { a: { color: 'red' } } }` or `{ '': { a: { color: 'red' } } }` without hashId,
  130. // we will get `&{a:{color:red;}}` or `{a:{color:red;}}` string for stylis to compile.
  131. // But it does not conform to stylis syntax,
  132. // and finally we will get `{color:red;}` as css, which is wrong.
  133. // So we need to remove key in root, and treat child `{ a: { color: 'red' } }` as root.
  134. mergedKey = '';
  135. nextRoot = true;
  136. }
  137. var _parseStyle3 = parseStyle(value, config, {
  138. root: nextRoot,
  139. injectHash: subInjectHash,
  140. parentSelectors: [].concat(_toConsumableArray(parentSelectors), [mergedKey])
  141. }),
  142. _parseStyle4 = _slicedToArray(_parseStyle3, 2),
  143. _parsedStr2 = _parseStyle4[0],
  144. childEffectStyle = _parseStyle4[1];
  145. effectStyle = _objectSpread(_objectSpread({}, effectStyle), childEffectStyle);
  146. styleStr += "".concat(mergedKey).concat(_parsedStr2);
  147. } else {
  148. var _value;
  149. function appendStyle(cssKey, cssValue) {
  150. if (process.env.NODE_ENV !== 'production' && (_typeof(value) !== 'object' || !(value !== null && value !== void 0 && value[SKIP_CHECK]))) {
  151. [contentQuotesLinter, hashedAnimationLinter].concat(_toConsumableArray(linters)).forEach(function (linter) {
  152. return linter(cssKey, cssValue, {
  153. path: path,
  154. hashId: hashId,
  155. parentSelectors: parentSelectors
  156. });
  157. });
  158. }
  159. // 如果是样式则直接插入
  160. var styleName = cssKey.replace(/[A-Z]/g, function (match) {
  161. return "-".concat(match.toLowerCase());
  162. });
  163. // Auto suffix with px
  164. var formatValue = cssValue;
  165. if (!unitless[cssKey] && typeof formatValue === 'number' && formatValue !== 0) {
  166. formatValue = "".concat(formatValue, "px");
  167. }
  168. // handle animationName & Keyframe value
  169. if (cssKey === 'animationName' && cssValue !== null && cssValue !== void 0 && cssValue._keyframe) {
  170. parseKeyframes(cssValue);
  171. formatValue = cssValue.getName(hashId);
  172. }
  173. styleStr += "".concat(styleName, ":").concat(formatValue, ";");
  174. }
  175. var actualValue = (_value = value === null || value === void 0 ? void 0 : value.value) !== null && _value !== void 0 ? _value : value;
  176. if (_typeof(value) === 'object' && value !== null && value !== void 0 && value[MULTI_VALUE] && Array.isArray(actualValue)) {
  177. actualValue.forEach(function (item) {
  178. appendStyle(key, item);
  179. });
  180. } else {
  181. appendStyle(key, actualValue);
  182. }
  183. }
  184. });
  185. }
  186. });
  187. if (!root) {
  188. styleStr = "{".concat(styleStr, "}");
  189. } else if (layer) {
  190. // fixme: https://github.com/thysultan/stylis/pull/339
  191. if (styleStr) {
  192. styleStr = "@layer ".concat(layer.name, " {").concat(styleStr, "}");
  193. }
  194. if (layer.dependencies) {
  195. effectStyle["@layer ".concat(layer.name)] = layer.dependencies.map(function (deps) {
  196. return "@layer ".concat(deps, ", ").concat(layer.name, ";");
  197. }).join('\n');
  198. }
  199. }
  200. return [styleStr, effectStyle];
  201. };
  202. // ============================================================================
  203. // == Register ==
  204. // ============================================================================
  205. export function uniqueHash(path, styleStr) {
  206. return hash("".concat(path.join('%')).concat(styleStr));
  207. }
  208. function Empty() {
  209. return null;
  210. }
  211. export var STYLE_PREFIX = 'style';
  212. /**
  213. * Register a style to the global style sheet.
  214. */
  215. export default function useStyleRegister(info, styleFn) {
  216. var token = info.token,
  217. path = info.path,
  218. hashId = info.hashId,
  219. layer = info.layer,
  220. nonce = info.nonce,
  221. clientOnly = info.clientOnly,
  222. _info$order = info.order,
  223. order = _info$order === void 0 ? 0 : _info$order;
  224. var _React$useContext = React.useContext(StyleContext),
  225. autoClear = _React$useContext.autoClear,
  226. mock = _React$useContext.mock,
  227. defaultCache = _React$useContext.defaultCache,
  228. hashPriority = _React$useContext.hashPriority,
  229. container = _React$useContext.container,
  230. ssrInline = _React$useContext.ssrInline,
  231. transformers = _React$useContext.transformers,
  232. linters = _React$useContext.linters,
  233. cache = _React$useContext.cache,
  234. enableLayer = _React$useContext.layer;
  235. var tokenKey = token._tokenKey;
  236. var fullPath = [tokenKey];
  237. if (enableLayer) {
  238. fullPath.push('layer');
  239. }
  240. fullPath.push.apply(fullPath, _toConsumableArray(path));
  241. // Check if need insert style
  242. var isMergedClientSide = isClientSide;
  243. if (process.env.NODE_ENV !== 'production' && mock !== undefined) {
  244. isMergedClientSide = mock === 'client';
  245. }
  246. var _useGlobalCache = useGlobalCache(STYLE_PREFIX, fullPath,
  247. // Create cache if needed
  248. function () {
  249. var cachePath = fullPath.join('|');
  250. // Get style from SSR inline style directly
  251. if (existPath(cachePath)) {
  252. var _getStyleAndHash = getStyleAndHash(cachePath),
  253. _getStyleAndHash2 = _slicedToArray(_getStyleAndHash, 2),
  254. inlineCacheStyleStr = _getStyleAndHash2[0],
  255. styleHash = _getStyleAndHash2[1];
  256. if (inlineCacheStyleStr) {
  257. return [inlineCacheStyleStr, tokenKey, styleHash, {}, clientOnly, order];
  258. }
  259. }
  260. // Generate style
  261. var styleObj = styleFn();
  262. var _parseStyle5 = parseStyle(styleObj, {
  263. hashId: hashId,
  264. hashPriority: hashPriority,
  265. layer: enableLayer ? layer : undefined,
  266. path: path.join('-'),
  267. transformers: transformers,
  268. linters: linters
  269. }),
  270. _parseStyle6 = _slicedToArray(_parseStyle5, 2),
  271. parsedStyle = _parseStyle6[0],
  272. effectStyle = _parseStyle6[1];
  273. var styleStr = normalizeStyle(parsedStyle);
  274. var styleId = uniqueHash(fullPath, styleStr);
  275. return [styleStr, tokenKey, styleId, effectStyle, clientOnly, order];
  276. },
  277. // Remove cache if no need
  278. function (_ref2, fromHMR) {
  279. var _ref3 = _slicedToArray(_ref2, 3),
  280. styleId = _ref3[2];
  281. if ((fromHMR || autoClear) && isClientSide) {
  282. removeCSS(styleId, {
  283. mark: ATTR_MARK,
  284. attachTo: container
  285. });
  286. }
  287. },
  288. // Effect: Inject style here
  289. function (_ref4) {
  290. var _ref5 = _slicedToArray(_ref4, 4),
  291. styleStr = _ref5[0],
  292. _ = _ref5[1],
  293. styleId = _ref5[2],
  294. effectStyle = _ref5[3];
  295. if (isMergedClientSide && styleStr !== CSS_FILE_STYLE) {
  296. var mergedCSSConfig = {
  297. mark: ATTR_MARK,
  298. prepend: enableLayer ? false : 'queue',
  299. attachTo: container,
  300. priority: order
  301. };
  302. var nonceStr = typeof nonce === 'function' ? nonce() : nonce;
  303. if (nonceStr) {
  304. mergedCSSConfig.csp = {
  305. nonce: nonceStr
  306. };
  307. }
  308. // ================= Split Effect Style =================
  309. // We will split effectStyle here since @layer should be at the top level
  310. var effectLayerKeys = [];
  311. var effectRestKeys = [];
  312. Object.keys(effectStyle).forEach(function (key) {
  313. if (key.startsWith('@layer')) {
  314. effectLayerKeys.push(key);
  315. } else {
  316. effectRestKeys.push(key);
  317. }
  318. });
  319. // ================= Inject Layer Style =================
  320. // Inject layer style
  321. effectLayerKeys.forEach(function (effectKey) {
  322. updateCSS(normalizeStyle(effectStyle[effectKey]), "_layer-".concat(effectKey), _objectSpread(_objectSpread({}, mergedCSSConfig), {}, {
  323. prepend: true
  324. }));
  325. });
  326. // ==================== Inject Style ====================
  327. // Inject style
  328. var style = updateCSS(styleStr, styleId, mergedCSSConfig);
  329. style[CSS_IN_JS_INSTANCE] = cache.instanceId;
  330. // Used for `useCacheToken` to remove on batch when token removed
  331. style.setAttribute(ATTR_TOKEN, tokenKey);
  332. // Debug usage. Dev only
  333. if (process.env.NODE_ENV !== 'production') {
  334. style.setAttribute(ATTR_CACHE_PATH, fullPath.join('|'));
  335. }
  336. // ================ Inject Effect Style =================
  337. // Inject client side effect style
  338. effectRestKeys.forEach(function (effectKey) {
  339. updateCSS(normalizeStyle(effectStyle[effectKey]), "_effect-".concat(effectKey), mergedCSSConfig);
  340. });
  341. }
  342. }),
  343. _useGlobalCache2 = _slicedToArray(_useGlobalCache, 3),
  344. cachedStyleStr = _useGlobalCache2[0],
  345. cachedTokenKey = _useGlobalCache2[1],
  346. cachedStyleId = _useGlobalCache2[2];
  347. return function (node) {
  348. var styleNode;
  349. if (!ssrInline || isMergedClientSide || !defaultCache) {
  350. styleNode = /*#__PURE__*/React.createElement(Empty, null);
  351. } else {
  352. styleNode = /*#__PURE__*/React.createElement("style", _extends({}, _defineProperty(_defineProperty({}, ATTR_TOKEN, cachedTokenKey), ATTR_MARK, cachedStyleId), {
  353. dangerouslySetInnerHTML: {
  354. __html: cachedStyleStr
  355. }
  356. }));
  357. }
  358. return /*#__PURE__*/React.createElement(React.Fragment, null, styleNode, node);
  359. };
  360. }
  361. export var extract = function extract(cache, effectStyles, options) {
  362. var _cache = _slicedToArray(cache, 6),
  363. styleStr = _cache[0],
  364. tokenKey = _cache[1],
  365. styleId = _cache[2],
  366. effectStyle = _cache[3],
  367. clientOnly = _cache[4],
  368. order = _cache[5];
  369. var _ref7 = options || {},
  370. plain = _ref7.plain;
  371. // Skip client only style
  372. if (clientOnly) {
  373. return null;
  374. }
  375. var keyStyleText = styleStr;
  376. // ====================== Share ======================
  377. // Used for rc-util
  378. var sharedAttrs = {
  379. 'data-rc-order': 'prependQueue',
  380. 'data-rc-priority': "".concat(order)
  381. };
  382. // ====================== Style ======================
  383. keyStyleText = toStyleStr(styleStr, tokenKey, styleId, sharedAttrs, plain);
  384. // =============== Create effect style ===============
  385. if (effectStyle) {
  386. Object.keys(effectStyle).forEach(function (effectKey) {
  387. // Effect style can be reused
  388. if (!effectStyles[effectKey]) {
  389. effectStyles[effectKey] = true;
  390. var effectStyleStr = normalizeStyle(effectStyle[effectKey]);
  391. var effectStyleHTML = toStyleStr(effectStyleStr, tokenKey, "_effect-".concat(effectKey), sharedAttrs, plain);
  392. if (effectKey.startsWith('@layer')) {
  393. keyStyleText = effectStyleHTML + keyStyleText;
  394. } else {
  395. keyStyleText += effectStyleHTML;
  396. }
  397. }
  398. });
  399. }
  400. return [order, styleId, keyStyleText];
  401. };