useCacheToken.js 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import hash from '@emotion/hash';
  3. import { ATTR_TOKEN, CSS_IN_JS_INSTANCE, useStyleInject } from '../StyleContext';
  4. import useGlobalCache from './useGlobalCache';
  5. import { flattenToken, token2key } from '../util';
  6. import { ref, computed } from 'vue';
  7. const EMPTY_OVERRIDE = {};
  8. const isProduction = process.env.NODE_ENV === 'production';
  9. // nuxt generate when NODE_ENV is prerender
  10. const isPrerender = process.env.NODE_ENV === 'prerender';
  11. // Generate different prefix to make user selector break in production env.
  12. // This helps developer not to do style override directly on the hash id.
  13. const hashPrefix = !isProduction && !isPrerender ? 'css-dev-only-do-not-override' : 'css';
  14. const tokenKeys = new Map();
  15. function recordCleanToken(tokenKey) {
  16. tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
  17. }
  18. function removeStyleTags(key, instanceId) {
  19. if (typeof document !== 'undefined') {
  20. const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
  21. styles.forEach(style => {
  22. var _a;
  23. if (style[CSS_IN_JS_INSTANCE] === instanceId) {
  24. (_a = style.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(style);
  25. }
  26. });
  27. }
  28. }
  29. const TOKEN_THRESHOLD = 0;
  30. // Remove will check current keys first
  31. function cleanTokenStyle(tokenKey, instanceId) {
  32. tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
  33. const tokenKeyList = Array.from(tokenKeys.keys());
  34. const cleanableKeyList = tokenKeyList.filter(key => {
  35. const count = tokenKeys.get(key) || 0;
  36. return count <= 0;
  37. });
  38. // Should keep tokens under threshold for not to insert style too often
  39. if (tokenKeyList.length - cleanableKeyList.length > TOKEN_THRESHOLD) {
  40. cleanableKeyList.forEach(key => {
  41. removeStyleTags(key, instanceId);
  42. tokenKeys.delete(key);
  43. });
  44. }
  45. }
  46. export const getComputedToken = (originToken, overrideToken, theme, format) => {
  47. const derivativeToken = theme.getDerivativeToken(originToken);
  48. // Merge with override
  49. let mergedDerivativeToken = _extends(_extends({}, derivativeToken), overrideToken);
  50. // Format if needed
  51. if (format) {
  52. mergedDerivativeToken = format(mergedDerivativeToken);
  53. }
  54. return mergedDerivativeToken;
  55. };
  56. /**
  57. * Cache theme derivative token as global shared one
  58. * @param theme Theme entity
  59. * @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
  60. * @param option Additional config
  61. * @returns Call Theme.getDerivativeToken(tokenObject) to get token
  62. */
  63. export default function useCacheToken(theme, tokens) {
  64. let option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ref({});
  65. const style = useStyleInject();
  66. // Basic - We do basic cache here
  67. const mergedToken = computed(() => _extends({}, ...tokens.value));
  68. const tokenStr = computed(() => flattenToken(mergedToken.value));
  69. const overrideTokenStr = computed(() => flattenToken(option.value.override || EMPTY_OVERRIDE));
  70. const cachedToken = useGlobalCache('token', computed(() => [option.value.salt || '', theme.value.id, tokenStr.value, overrideTokenStr.value]), () => {
  71. const {
  72. salt = '',
  73. override = EMPTY_OVERRIDE,
  74. formatToken,
  75. getComputedToken: compute
  76. } = option.value;
  77. const mergedDerivativeToken = compute ? compute(mergedToken.value, override, theme.value) : getComputedToken(mergedToken.value, override, theme.value, formatToken);
  78. // Optimize for `useStyleRegister` performance
  79. const tokenKey = token2key(mergedDerivativeToken, salt);
  80. mergedDerivativeToken._tokenKey = tokenKey;
  81. recordCleanToken(tokenKey);
  82. const hashId = `${hashPrefix}-${hash(tokenKey)}`;
  83. mergedDerivativeToken._hashId = hashId; // Not used
  84. return [mergedDerivativeToken, hashId];
  85. }, cache => {
  86. var _a;
  87. // Remove token will remove all related style
  88. cleanTokenStyle(cache[0]._tokenKey, (_a = style.value) === null || _a === void 0 ? void 0 : _a.cache.instanceId);
  89. });
  90. return cachedToken;
  91. }