@acusti/styling 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Style.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- /// <reference types="react" />
1
+ import * as React from 'react';
2
2
  type Props = {
3
3
  children: string;
4
4
  };
5
- declare const Style: ({ children }: Props) => JSX.Element | null;
5
+ declare const Style: ({ children }: Props) => React.JSX.Element | null;
6
6
  export default Style;
package/dist/Style.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import * as React from 'react';
2
- import { unregisterStyles, updateStyles } from './style-registry.js';
2
+ import { getRegisteredStyles, registerStyles, unregisterStyles, updateStyles, } from './style-registry.js';
3
3
  const { useCallback, useEffect, useMemo, useRef, useState } = React;
4
4
  const Style = ({ children }) => {
5
5
  // Minify CSS styles by replacing consecutive whitespace (including \n) with ' '
6
6
  const styles = useMemo(() => children.replace(/\s+/gm, ' '), [children]);
7
7
  const [ownerDocument, setOwnerDocument] = useState(null);
8
+ const isMountedRef = useRef(false);
9
+ useEffect(() => {
10
+ isMountedRef.current = true;
11
+ unregisterStyles({ ownerDocument: 'global', styles });
12
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
8
13
  useEffect(() => () => {
9
14
  if (!ownerDocument)
10
15
  return;
@@ -28,9 +33,13 @@ const Style = ({ children }) => {
28
33
  }, []);
29
34
  if (ownerDocument)
30
35
  return null;
31
- return (React.createElement("style", { dangerouslySetInnerHTML: {
32
- __html: styles,
33
- }, ref: handleRef }));
36
+ // Avoid duplicate style rendering during SSR via style registry
37
+ if (!isMountedRef.current) {
38
+ if (getRegisteredStyles({ ownerDocument: 'global', styles }))
39
+ return null;
40
+ registerStyles({ ownerDocument: 'global', styles });
41
+ }
42
+ return React.createElement("style", { dangerouslySetInnerHTML: { __html: styles }, ref: handleRef });
34
43
  };
35
44
  export default Style;
36
45
  //# sourceMappingURL=Style.js.map
@@ -5,8 +5,9 @@
5
5
  * @flow
6
6
  */
7
7
 
8
+ import * as React from "react";
8
9
  declare type Props = {|
9
10
  children: string,
10
11
  |};
11
- declare var Style: (x: Props) => React$Node | null;
12
+ declare var Style: (x: Props) => React.JSX.Element | null;
12
13
  declare export default typeof Style;
package/dist/Style.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Style.js","sourceRoot":"","sources":["../src/Style.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;AAMpE,MAAM,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAS,EAAE,EAAE;IAClC,gFAAgF;IAChF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAC;IAE1E,SAAS,CACL,GAAG,EAAE,CAAC,GAAG,EAAE;QACP,IAAI,CAAC,aAAa;YAAE,OAAO;QAC3B,gBAAgB,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC,EACD,CAAC,aAAa,CAAC,CAClB,CAAC;IAEF,MAAM,iBAAiB,GAAG,MAAM,CAAS,EAAE,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,YAAY,CAAC;YACT,aAAa;YACb,cAAc,EAAE,iBAAiB,CAAC,OAAO;YACzC,MAAM;SACT,CAAC,CAAC;QAEH,iBAAiB,CAAC,OAAO,GAAG,MAAM,CAAC;IACvC,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAE5B,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,OAA2B,EAAE,EAAE;QAC1D,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,gBAAgB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,CACH,+BACI,uBAAuB,EAAE;YACrB,MAAM,EAAE,MAAM;SACjB,EACD,GAAG,EAAE,SAAS,GAChB,CACL,CAAC;AACN,CAAC,CAAC;AAEF,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"Style.js","sourceRoot":"","sources":["../src/Style.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EACH,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,YAAY,GACf,MAAM,qBAAqB,CAAC;AAE7B,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;AAMpE,MAAM,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAS,EAAE,EAAE;IAClC,gFAAgF;IAChF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAC;IAC1E,MAAM,YAAY,GAAG,MAAM,CAAU,KAAK,CAAC,CAAC;IAE5C,SAAS,CAAC,GAAG,EAAE;QACX,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;QAC5B,gBAAgB,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kDAAkD;IAE1D,SAAS,CACL,GAAG,EAAE,CAAC,GAAG,EAAE;QACP,IAAI,CAAC,aAAa;YAAE,OAAO;QAC3B,gBAAgB,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC,EACD,CAAC,aAAa,CAAC,CAClB,CAAC;IAEF,MAAM,iBAAiB,GAAG,MAAM,CAAS,EAAE,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,YAAY,CAAC;YACT,aAAa;YACb,cAAc,EAAE,iBAAiB,CAAC,OAAO;YACzC,MAAM;SACT,CAAC,CAAC;QAEH,iBAAiB,CAAC,OAAO,GAAG,MAAM,CAAC;IACvC,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAE5B,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,OAA2B,EAAE,EAAE;QAC1D,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,gBAAgB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAE/B,gEAAgE;IAChE,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QACxB,IAAI,mBAAmB,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1E,cAAc,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,+BAAO,uBAAuB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,GAAI,CAAC;AAClF,CAAC,CAAC;AAEF,eAAe,KAAK,CAAC"}
@@ -1,7 +1,11 @@
1
1
  type Payload = {
2
- ownerDocument: Document;
2
+ ownerDocument: Document | 'global';
3
3
  styles: string;
4
4
  };
5
+ export declare const getRegisteredStyles: ({ ownerDocument, styles }: Payload) => {
6
+ element: HTMLStyleElement | null;
7
+ referenceCount: number;
8
+ } | null;
5
9
  export declare const registerStyles: ({ ownerDocument, styles }: Payload) => void;
6
10
  export declare const unregisterStyles: ({ ownerDocument, styles }: Payload) => void;
7
11
  type UpdatePayload = {
@@ -1,18 +1,31 @@
1
1
  const styleRegistry = new Map();
2
+ export const getRegisteredStyles = ({ ownerDocument, styles }) => {
3
+ var _a;
4
+ if (!styles)
5
+ return null;
6
+ const stylesMap = styleRegistry.get(styles);
7
+ if (!stylesMap)
8
+ return null;
9
+ return (_a = stylesMap.get(ownerDocument)) !== null && _a !== void 0 ? _a : null;
10
+ };
2
11
  export const registerStyles = ({ ownerDocument, styles }) => {
3
12
  if (!styles)
4
13
  return;
5
- const stylesMap = styleRegistry.get(styles);
6
- const existingStylesItem = stylesMap === null || stylesMap === void 0 ? void 0 : stylesMap.get(ownerDocument);
14
+ const existingStylesItem = getRegisteredStyles({ ownerDocument, styles });
7
15
  if (existingStylesItem) {
8
16
  existingStylesItem.referenceCount++;
9
17
  return;
10
18
  }
19
+ if (ownerDocument === 'global') {
20
+ styleRegistry.set(styles, new Map([[ownerDocument, { element: null, referenceCount: 1 }]]));
21
+ return;
22
+ }
11
23
  const element = ownerDocument.createElement('style');
12
24
  element.setAttribute('data-ukt-styling', '');
13
25
  element.innerHTML = styles;
14
26
  ownerDocument.head.appendChild(element);
15
27
  const stylesItem = { element, referenceCount: 1 };
28
+ const stylesMap = styleRegistry.get(styles);
16
29
  if (stylesMap) {
17
30
  stylesMap.set(ownerDocument, stylesItem);
18
31
  return;
@@ -22,19 +35,21 @@ export const registerStyles = ({ ownerDocument, styles }) => {
22
35
  export const unregisterStyles = ({ ownerDocument, styles }) => {
23
36
  if (!styles)
24
37
  return;
25
- const stylesMap = styleRegistry.get(styles);
26
- const stylesItem = stylesMap === null || stylesMap === void 0 ? void 0 : stylesMap.get(ownerDocument);
27
- if (!stylesMap || !stylesItem)
38
+ const stylesItem = getRegisteredStyles({ ownerDocument, styles });
39
+ if (!stylesItem)
28
40
  return;
29
41
  stylesItem.referenceCount--;
30
42
  if (stylesItem.referenceCount)
31
43
  return;
32
44
  // If no more references to these styles in this document, remove <style> element from the DOM
33
- const { parentElement } = stylesItem.element;
34
- if (parentElement) {
35
- parentElement.removeChild(stylesItem.element);
45
+ if (stylesItem.element) {
46
+ const { parentElement } = stylesItem.element;
47
+ if (parentElement) {
48
+ parentElement.removeChild(stylesItem.element);
49
+ }
36
50
  }
37
51
  // Then remove the document Map
52
+ const stylesMap = styleRegistry.get(styles);
38
53
  stylesMap.delete(ownerDocument);
39
54
  if (stylesMap.size)
40
55
  return;
@@ -44,6 +59,19 @@ export const unregisterStyles = ({ ownerDocument, styles }) => {
44
59
  export const updateStyles = ({ ownerDocument, previousStyles, styles, }) => {
45
60
  if (previousStyles === styles)
46
61
  return;
62
+ const stylesMap = styleRegistry.get(previousStyles);
63
+ const stylesItem = stylesMap === null || stylesMap === void 0 ? void 0 : stylesMap.get(ownerDocument);
64
+ if (stylesMap && (stylesItem === null || stylesItem === void 0 ? void 0 : stylesItem.element) && (stylesItem === null || stylesItem === void 0 ? void 0 : stylesItem.referenceCount) === 1) {
65
+ // Mutate existing <style> element with updated styles
66
+ stylesItem.element.innerHTML = styles;
67
+ styleRegistry.set(styles, new Map([[ownerDocument, stylesItem]]));
68
+ // Cleanup previous stylesMap
69
+ stylesMap.delete(ownerDocument);
70
+ if (!stylesMap.size) {
71
+ styleRegistry.delete(previousStyles);
72
+ }
73
+ return;
74
+ }
47
75
  if (previousStyles) {
48
76
  unregisterStyles({ ownerDocument, styles: previousStyles });
49
77
  }
@@ -6,9 +6,13 @@
6
6
  */
7
7
 
8
8
  declare type Payload = {|
9
- ownerDocument: Document,
9
+ ownerDocument: Document | "global",
10
10
  styles: string,
11
11
  |};
12
+ declare export var getRegisteredStyles: (x: Payload) => {|
13
+ element: HTMLStyleElement | null,
14
+ referenceCount: number,
15
+ |} | null;
12
16
  declare export var registerStyles: (x: Payload) => void;
13
17
  declare export var unregisterStyles: (x: Payload) => void;
14
18
  declare type UpdatePayload = {|
@@ -1 +1 @@
1
- {"version":3,"file":"style-registry.js","sourceRoot":"","sources":["../src/style-registry.ts"],"names":[],"mappings":"AAKA,MAAM,aAAa,GAAkB,IAAI,GAAG,EAAE,CAAC;AAI/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,aAAa,EAAE,MAAM,EAAW,EAAE,EAAE;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,kBAAkB,GAAG,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IAEzD,IAAI,kBAAkB,EAAE;QACpB,kBAAkB,CAAC,cAAc,EAAE,CAAC;QACpC,OAAO;KACV;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACrD,OAAO,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC;IAC3B,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;IAElD,IAAI,SAAS,EAAE;QACX,SAAS,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QACzC,OAAO;KACV;IAED,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAAE,aAAa,EAAE,MAAM,EAAW,EAAE,EAAE;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IACjD,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU;QAAE,OAAO;IAEtC,UAAU,CAAC,cAAc,EAAE,CAAC;IAC5B,IAAI,UAAU,CAAC,cAAc;QAAE,OAAO;IAEtC,8FAA8F;IAC9F,MAAM,EAAE,aAAa,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAC7C,IAAI,aAAa,EAAE;QACf,aAAa,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;KACjD;IACD,+BAA+B;IAC/B,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAEhC,IAAI,SAAS,CAAC,IAAI;QAAE,OAAO;IAC3B,4EAA4E;IAC5E,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC,CAAC;AAIF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EACzB,aAAa,EACb,cAAc,EACd,MAAM,GACM,EAAE,EAAE;IAChB,IAAI,cAAc,KAAK,MAAM;QAAE,OAAO;IAEtC,IAAI,cAAc,EAAE;QAChB,gBAAgB,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;KAC/D;IAED,cAAc,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC"}
1
+ {"version":3,"file":"style-registry.js","sourceRoot":"","sources":["../src/style-registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acusti/styling",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "React component that renders a CSS style string as a <style> element in the <head> of the document (with global style de-duplication)",
5
5
  "keywords": [
6
6
  "react",
@@ -34,8 +34,9 @@
34
34
  },
35
35
  "homepage": "https://github.com/acusti/uikit/tree/main/packages/styling#readme",
36
36
  "devDependencies": {
37
- "@types/react": "^18.0.25",
38
- "typescript": "^4.9.3"
37
+ "@types/react": "^18.2.45",
38
+ "typescript": "^5.3.3",
39
+ "vitest": "^1.1.0"
39
40
  },
40
41
  "peerDependencies": {
41
42
  "react": "^16.8 || ^17 || ^18",
package/src/Style.tsx CHANGED
@@ -1,6 +1,11 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { unregisterStyles, updateStyles } from './style-registry.js';
3
+ import {
4
+ getRegisteredStyles,
5
+ registerStyles,
6
+ unregisterStyles,
7
+ updateStyles,
8
+ } from './style-registry.js';
4
9
 
5
10
  const { useCallback, useEffect, useMemo, useRef, useState } = React;
6
11
 
@@ -12,6 +17,12 @@ const Style = ({ children }: Props) => {
12
17
  // Minify CSS styles by replacing consecutive whitespace (including \n) with ' '
13
18
  const styles = useMemo(() => children.replace(/\s+/gm, ' '), [children]);
14
19
  const [ownerDocument, setOwnerDocument] = useState<Document | null>(null);
20
+ const isMountedRef = useRef<boolean>(false);
21
+
22
+ useEffect(() => {
23
+ isMountedRef.current = true;
24
+ unregisterStyles({ ownerDocument: 'global', styles });
25
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
15
26
 
16
27
  useEffect(
17
28
  () => () => {
@@ -42,14 +53,13 @@ const Style = ({ children }: Props) => {
42
53
 
43
54
  if (ownerDocument) return null;
44
55
 
45
- return (
46
- <style
47
- dangerouslySetInnerHTML={{
48
- __html: styles,
49
- }}
50
- ref={handleRef}
51
- />
52
- );
56
+ // Avoid duplicate style rendering during SSR via style registry
57
+ if (!isMountedRef.current) {
58
+ if (getRegisteredStyles({ ownerDocument: 'global', styles })) return null;
59
+ registerStyles({ ownerDocument: 'global', styles });
60
+ }
61
+
62
+ return <style dangerouslySetInnerHTML={{ __html: styles }} ref={handleRef} />;
53
63
  };
54
64
 
55
65
  export default Style;
@@ -1,29 +1,43 @@
1
1
  type StyleRegistry = Map<
2
2
  string,
3
- Map<Document, { element: HTMLStyleElement; referenceCount: number }>
3
+ Map<Document | 'global', { element: HTMLStyleElement | null; referenceCount: number }>
4
4
  >;
5
5
 
6
6
  const styleRegistry: StyleRegistry = new Map();
7
7
 
8
- type Payload = { ownerDocument: Document; styles: string };
8
+ type Payload = { ownerDocument: Document | 'global'; styles: string };
9
+
10
+ export const getRegisteredStyles = ({ ownerDocument, styles }: Payload) => {
11
+ if (!styles) return null;
12
+ const stylesMap = styleRegistry.get(styles);
13
+ if (!stylesMap) return null;
14
+ return stylesMap.get(ownerDocument) ?? null;
15
+ };
9
16
 
10
17
  export const registerStyles = ({ ownerDocument, styles }: Payload) => {
11
18
  if (!styles) return;
12
19
 
13
- const stylesMap = styleRegistry.get(styles);
14
- const existingStylesItem = stylesMap?.get(ownerDocument);
15
-
20
+ const existingStylesItem = getRegisteredStyles({ ownerDocument, styles });
16
21
  if (existingStylesItem) {
17
22
  existingStylesItem.referenceCount++;
18
23
  return;
19
24
  }
20
25
 
26
+ if (ownerDocument === 'global') {
27
+ styleRegistry.set(
28
+ styles,
29
+ new Map([[ownerDocument, { element: null, referenceCount: 1 }]]),
30
+ );
31
+ return;
32
+ }
33
+
21
34
  const element = ownerDocument.createElement('style');
22
35
  element.setAttribute('data-ukt-styling', '');
23
36
  element.innerHTML = styles;
24
37
  ownerDocument.head.appendChild(element);
25
38
  const stylesItem = { element, referenceCount: 1 };
26
39
 
40
+ const stylesMap = styleRegistry.get(styles);
27
41
  if (stylesMap) {
28
42
  stylesMap.set(ownerDocument, stylesItem);
29
43
  return;
@@ -35,19 +49,22 @@ export const registerStyles = ({ ownerDocument, styles }: Payload) => {
35
49
  export const unregisterStyles = ({ ownerDocument, styles }: Payload) => {
36
50
  if (!styles) return;
37
51
 
38
- const stylesMap = styleRegistry.get(styles);
39
- const stylesItem = stylesMap?.get(ownerDocument);
40
- if (!stylesMap || !stylesItem) return;
52
+ const stylesItem = getRegisteredStyles({ ownerDocument, styles });
53
+ if (!stylesItem) return;
41
54
 
42
55
  stylesItem.referenceCount--;
43
56
  if (stylesItem.referenceCount) return;
44
57
 
45
58
  // If no more references to these styles in this document, remove <style> element from the DOM
46
- const { parentElement } = stylesItem.element;
47
- if (parentElement) {
48
- parentElement.removeChild(stylesItem.element);
59
+ if (stylesItem.element) {
60
+ const { parentElement } = stylesItem.element;
61
+ if (parentElement) {
62
+ parentElement.removeChild(stylesItem.element);
63
+ }
49
64
  }
65
+
50
66
  // Then remove the document Map
67
+ const stylesMap = styleRegistry.get(styles)!;
51
68
  stylesMap.delete(ownerDocument);
52
69
 
53
70
  if (stylesMap.size) return;
@@ -64,6 +81,20 @@ export const updateStyles = ({
64
81
  }: UpdatePayload) => {
65
82
  if (previousStyles === styles) return;
66
83
 
84
+ const stylesMap = styleRegistry.get(previousStyles);
85
+ const stylesItem = stylesMap?.get(ownerDocument);
86
+ if (stylesMap && stylesItem?.element && stylesItem?.referenceCount === 1) {
87
+ // Mutate existing <style> element with updated styles
88
+ stylesItem.element.innerHTML = styles;
89
+ styleRegistry.set(styles, new Map([[ownerDocument, stylesItem]]));
90
+ // Cleanup previous stylesMap
91
+ stylesMap.delete(ownerDocument);
92
+ if (!stylesMap.size) {
93
+ styleRegistry.delete(previousStyles);
94
+ }
95
+ return;
96
+ }
97
+
67
98
  if (previousStyles) {
68
99
  unregisterStyles({ ownerDocument, styles: previousStyles });
69
100
  }