dynamicCSS.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import canUseDom from '../../_util/canUseDom';
  2. import contains from './contains';
  3. const APPEND_ORDER = 'data-vc-order';
  4. const MARK_KEY = `vc-util-key`;
  5. const containerCache = new Map();
  6. function getMark() {
  7. let {
  8. mark
  9. } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  10. if (mark) {
  11. return mark.startsWith('data-') ? mark : `data-${mark}`;
  12. }
  13. return MARK_KEY;
  14. }
  15. function getContainer(option) {
  16. if (option.attachTo) {
  17. return option.attachTo;
  18. }
  19. const head = document.querySelector('head');
  20. return head || document.body;
  21. }
  22. function getOrder(prepend) {
  23. if (prepend === 'queue') {
  24. return 'prependQueue';
  25. }
  26. return prepend ? 'prepend' : 'append';
  27. }
  28. /**
  29. * Find style which inject by rc-util
  30. */
  31. function findStyles(container) {
  32. return Array.from((containerCache.get(container) || container).children).filter(node => node.tagName === 'STYLE');
  33. }
  34. export function injectCSS(css) {
  35. let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  36. if (!canUseDom()) {
  37. return null;
  38. }
  39. const {
  40. csp,
  41. prepend
  42. } = option;
  43. const styleNode = document.createElement('style');
  44. styleNode.setAttribute(APPEND_ORDER, getOrder(prepend));
  45. if (csp === null || csp === void 0 ? void 0 : csp.nonce) {
  46. styleNode.nonce = csp === null || csp === void 0 ? void 0 : csp.nonce;
  47. }
  48. styleNode.innerHTML = css;
  49. const container = getContainer(option);
  50. const {
  51. firstChild
  52. } = container;
  53. if (prepend) {
  54. // If is queue `prepend`, it will prepend first style and then append rest style
  55. if (prepend === 'queue') {
  56. const existStyle = findStyles(container).filter(node => ['prepend', 'prependQueue'].includes(node.getAttribute(APPEND_ORDER)));
  57. if (existStyle.length) {
  58. container.insertBefore(styleNode, existStyle[existStyle.length - 1].nextSibling);
  59. return styleNode;
  60. }
  61. }
  62. // Use `insertBefore` as `prepend`
  63. container.insertBefore(styleNode, firstChild);
  64. } else {
  65. container.appendChild(styleNode);
  66. }
  67. return styleNode;
  68. }
  69. function findExistNode(key) {
  70. let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  71. const container = getContainer(option);
  72. return findStyles(container).find(node => node.getAttribute(getMark(option)) === key);
  73. }
  74. export function removeCSS(key) {
  75. let option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  76. const existNode = findExistNode(key, option);
  77. if (existNode) {
  78. const container = getContainer(option);
  79. container.removeChild(existNode);
  80. }
  81. }
  82. /**
  83. * qiankun will inject `appendChild` to insert into other
  84. */
  85. function syncRealContainer(container, option) {
  86. const cachedRealContainer = containerCache.get(container);
  87. // Find real container when not cached or cached container removed
  88. if (!cachedRealContainer || !contains(document, cachedRealContainer)) {
  89. const placeholderStyle = injectCSS('', option);
  90. const {
  91. parentNode
  92. } = placeholderStyle;
  93. containerCache.set(container, parentNode);
  94. container.removeChild(placeholderStyle);
  95. }
  96. }
  97. /**
  98. * manually clear container cache to avoid global cache in unit testes
  99. */
  100. export function clearContainerCache() {
  101. containerCache.clear();
  102. }
  103. export function updateCSS(css, key) {
  104. let option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  105. var _a, _b, _c;
  106. const container = getContainer(option);
  107. // Sync real parent
  108. syncRealContainer(container, option);
  109. const existNode = findExistNode(key, option);
  110. if (existNode) {
  111. if (((_a = option.csp) === null || _a === void 0 ? void 0 : _a.nonce) && existNode.nonce !== ((_b = option.csp) === null || _b === void 0 ? void 0 : _b.nonce)) {
  112. existNode.nonce = (_c = option.csp) === null || _c === void 0 ? void 0 : _c.nonce;
  113. }
  114. if (existNode.innerHTML !== css) {
  115. existNode.innerHTML = css;
  116. }
  117. return existNode;
  118. }
  119. const newNode = injectCSS(css, option);
  120. newNode.setAttribute(getMark(option), key);
  121. return newNode;
  122. }