@lomray/react-head-manager 1.0.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/context.d.ts +17 -0
- package/context.js +2 -0
- package/context.js.map +1 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/manager.d.ts +158 -0
- package/manager.js +2 -0
- package/manager.js.map +1 -0
- package/package.json +85 -0
- package/server/index.d.ts +30 -0
- package/server/index.js +2 -0
- package/server/index.js.map +1 -0
package/context.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import Manager from "./manager.js";
|
|
3
|
+
interface IMetaManager {
|
|
4
|
+
manager: Manager;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Global application meta manager provider
|
|
8
|
+
* @constructor
|
|
9
|
+
*/
|
|
10
|
+
declare const MetaManagerProvider: FC<PropsWithChildren<IMetaManager>>;
|
|
11
|
+
declare const useMetaManager: () => IMetaManager;
|
|
12
|
+
/**
|
|
13
|
+
* Meta tag component
|
|
14
|
+
* @constructor
|
|
15
|
+
*/
|
|
16
|
+
declare const Meta: FC<PropsWithChildren>;
|
|
17
|
+
export { Meta, MetaManagerProvider, useMetaManager };
|
package/context.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@lomray/consistent-suspense"),r=require("react"),t=require("./manager.js");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=a(r);const s=n.default.createContext({manager:new t}),u=()=>r.useContext(s);exports.Meta=t=>{let{children:a}=t;const{manager:n}=u(),s=e.useId();return n.isServer&&n.pushTags(a,s),r.useEffect((()=>(n.pushTags(a,s),()=>n.removeTags(s))),[n,a]),null},exports.MetaManagerProvider=e=>{let{children:t,manager:a}=e;r.useEffect((()=>{a.analyzeClientHead()}),[]);const u=r.useMemo((()=>({manager:a})),[a]);return n.default.createElement(s.Provider,{value:u,children:t})},exports.useMetaManager=u;
|
|
2
|
+
//# sourceMappingURL=context.js.map
|
package/context.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sources":["../src/context.tsx"],"sourcesContent":["import { useId } from '@lomray/consistent-suspense';\nimport type { FC, PropsWithChildren } from 'react';\nimport React, { useContext, useEffect, useMemo } from 'react';\nimport Manager from './manager';\n\ninterface IMetaManager {\n manager: Manager;\n}\n\n/**\n * Global meta manager context\n */\nconst MetaManagerContext = React.createContext<IMetaManager>({ manager: new Manager() });\n\n/**\n * Global application meta manager provider\n * @constructor\n */\nconst MetaManagerProvider: FC<PropsWithChildren<IMetaManager>> = ({ children, manager }) => {\n /**\n * Analyze client default meta tags\n */\n useEffect(() => {\n manager.analyzeClientHead();\n }, []);\n\n const value = useMemo(() => ({ manager }), [manager]);\n\n return <MetaManagerContext.Provider value={value} children={children} />;\n};\n\nconst useMetaManager = () => useContext(MetaManagerContext);\n\n/**\n * Meta tag component\n * @constructor\n */\nconst Meta: FC<PropsWithChildren> = ({ children }) => {\n const { manager } = useMetaManager();\n const containerId = useId();\n\n /**\n * Server side push\n */\n if (manager.isServer) {\n manager.pushTags(children, containerId);\n }\n\n /**\n * Client side push\n */\n useEffect(() => {\n manager.pushTags(children, containerId);\n\n return () => manager.removeTags(containerId);\n }, [manager, children]);\n\n return null;\n};\n\nexport { Meta, MetaManagerProvider, useMetaManager };\n"],"names":["MetaManagerContext","React","createContext","manager","Manager","useMetaManager","useContext","_ref2","children","containerId","useId","isServer","pushTags","useEffect","removeTags","_ref","analyzeClientHead","value","useMemo","createElement","Provider"],"mappings":"iPAYA,MAAMA,EAAqBC,EAAK,QAACC,cAA4B,CAAEC,QAAS,IAAIC,IAmBtEC,EAAiBA,IAAMC,EAAUA,WAACN,gBAMJO,IAAiB,IAAhBC,SAAEA,GAAUD,EAC/C,MAAMJ,QAAEA,GAAYE,IACdI,EAAcC,EAAAA,QAkBpB,OAbIP,EAAQQ,UACVR,EAAQS,SAASJ,EAAUC,GAM7BI,EAAAA,WAAU,KACRV,EAAQS,SAASJ,EAAUC,GAEpB,IAAMN,EAAQW,WAAWL,KAC/B,CAACN,EAASK,IAEN,IAAI,8BAvCoDO,IAA0B,IAAzBP,SAAEA,EAAQL,QAAEA,GAASY,EAIrFF,EAAAA,WAAU,KACRV,EAAQa,mBAAmB,GAC1B,IAEH,MAAMC,EAAQC,EAAAA,SAAQ,KAAO,CAAEf,aAAY,CAACA,IAE5C,OAAOF,UAAAkB,cAACnB,EAAmBoB,SAAQ,CAACH,MAAOA,EAAOT,SAAUA,GAAY"}
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("./context.js"),r=require("./manager.js");exports.Meta=e.Meta,exports.MetaManagerProvider=e.MetaManagerProvider,exports.useMetaManager=e.useMetaManager,exports.Manager=r;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/manager.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
declare enum TagStatus {
|
|
3
|
+
init = "init",
|
|
4
|
+
synced = "synced",
|
|
5
|
+
drain = "drain"
|
|
6
|
+
}
|
|
7
|
+
interface IMetaManagerTags {
|
|
8
|
+
html: Map<string, Record<string, any>>;
|
|
9
|
+
body: Map<string, Record<string, any>>;
|
|
10
|
+
meta: Map<string, {
|
|
11
|
+
element?: ReactElement;
|
|
12
|
+
domElement?: HTMLElement;
|
|
13
|
+
order: number;
|
|
14
|
+
containerId: string;
|
|
15
|
+
status: TagStatus;
|
|
16
|
+
}>;
|
|
17
|
+
containers: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
interface IMetaManagerTagsDefinitions {
|
|
20
|
+
[type: string]: {
|
|
21
|
+
key?: string;
|
|
22
|
+
order: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Meta tags manager
|
|
27
|
+
*/
|
|
28
|
+
declare class Manager {
|
|
29
|
+
/**
|
|
30
|
+
* Root container id
|
|
31
|
+
*/
|
|
32
|
+
static rootContainerId: string;
|
|
33
|
+
/**
|
|
34
|
+
* Detect server side
|
|
35
|
+
*/
|
|
36
|
+
isServer: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Meta tags state
|
|
39
|
+
*/
|
|
40
|
+
protected tags: IMetaManagerTags;
|
|
41
|
+
/**
|
|
42
|
+
* Tags definitions
|
|
43
|
+
*/
|
|
44
|
+
protected tagsDefinitions: IMetaManagerTagsDefinitions;
|
|
45
|
+
/**
|
|
46
|
+
* @constructor
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* @constructor
|
|
50
|
+
*/
|
|
51
|
+
constructor(tags?: Partial<Manager['tags']>);
|
|
52
|
+
/**
|
|
53
|
+
* Set tags definitions
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* Set tags definitions
|
|
57
|
+
*/
|
|
58
|
+
setTagsDefinitions(definitions: IMetaManagerTagsDefinitions): void;
|
|
59
|
+
/**
|
|
60
|
+
* Remove children, dangerouslySetInnerHTML etc.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Remove children, dangerouslySetInnerHTML etc.
|
|
64
|
+
*/
|
|
65
|
+
static cleanupElementProps(props: Record<string, any>): Record<string, any>;
|
|
66
|
+
/**
|
|
67
|
+
* Get meta tags
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Get meta tags
|
|
71
|
+
*/
|
|
72
|
+
getTags(): IMetaManagerTags;
|
|
73
|
+
/**
|
|
74
|
+
* Replace react attribute to valid DOM attribute
|
|
75
|
+
*/
|
|
76
|
+
/**
|
|
77
|
+
* Replace react attribute to valid DOM attribute
|
|
78
|
+
*/
|
|
79
|
+
protected replaceAttribute(tagName: string, attribute: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Create DOM element from React element
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
84
|
+
* Create DOM element from React element
|
|
85
|
+
*/
|
|
86
|
+
protected createDomElement(element: ReactElement): HTMLElement | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Make element key by element props
|
|
89
|
+
*/
|
|
90
|
+
/**
|
|
91
|
+
* Make element key by element props
|
|
92
|
+
*/
|
|
93
|
+
protected buildKeyByProps(type: string, props?: Record<string, any>, isSkipProps?: boolean): string | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Get default tag key for not unique elements
|
|
96
|
+
*/
|
|
97
|
+
/**
|
|
98
|
+
* Get default tag key for not unique elements
|
|
99
|
+
*/
|
|
100
|
+
protected getDefaultKey(type: string, containerId: string, index: number): string;
|
|
101
|
+
/**
|
|
102
|
+
* Check if tag is not unique
|
|
103
|
+
*/
|
|
104
|
+
/**
|
|
105
|
+
* Check if tag is not unique
|
|
106
|
+
*/
|
|
107
|
+
protected isNotUniqueTag(key: string): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Push react elements to meta state
|
|
110
|
+
*/
|
|
111
|
+
/**
|
|
112
|
+
* Push react elements to meta state
|
|
113
|
+
*/
|
|
114
|
+
protected pushElements(elements: ReactNode, containerId: string, isReplace?: boolean, status?: TagStatus): void;
|
|
115
|
+
/**
|
|
116
|
+
* Get html or body props
|
|
117
|
+
*/
|
|
118
|
+
/**
|
|
119
|
+
* Get html or body props
|
|
120
|
+
*/
|
|
121
|
+
getRootTagProps(props: Manager['tags']['html']): Record<string, any>;
|
|
122
|
+
/**
|
|
123
|
+
* Apply props to dom element
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* Apply props to dom element
|
|
127
|
+
*/
|
|
128
|
+
protected applyDomElementAttributes(element: Element, props?: Record<string, any>): void;
|
|
129
|
+
/**
|
|
130
|
+
* Synchronize local meta tags with head meta tags
|
|
131
|
+
*/
|
|
132
|
+
/**
|
|
133
|
+
* Synchronize local meta tags with head meta tags
|
|
134
|
+
*/
|
|
135
|
+
protected syncMeta(containerId?: string): void;
|
|
136
|
+
/**
|
|
137
|
+
* Push new meta tags
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* Push new meta tags
|
|
141
|
+
*/
|
|
142
|
+
pushTags(elements: ReactNode, containerId: string, isReplace?: boolean): void;
|
|
143
|
+
/**
|
|
144
|
+
* Remove meta tags
|
|
145
|
+
*/
|
|
146
|
+
/**
|
|
147
|
+
* Remove meta tags
|
|
148
|
+
*/
|
|
149
|
+
removeTags(containerId: string): void;
|
|
150
|
+
/**
|
|
151
|
+
* Initial analyze client head meta tags
|
|
152
|
+
*/
|
|
153
|
+
/**
|
|
154
|
+
* Initial analyze client head meta tags
|
|
155
|
+
*/
|
|
156
|
+
analyzeClientHead(): void;
|
|
157
|
+
}
|
|
158
|
+
export { Manager as default, IMetaManagerTags, IMetaManagerTagsDefinitions };
|
package/manager.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("@babel/runtime/helpers/defineProperty"),t=require("html-react-parser"),n=require("react");function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o,r=i(e),s=i(t),a=i(n);!function(e){e.init="init",e.synced="synced",e.drain="drain"}(o||(o={}));class l{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r.default(this,"isServer","undefined"==typeof window),r.default(this,"tags",void 0),r.default(this,"tagsDefinitions",{"meta[charset]":{order:10},"meta[name='viewport']":{order:20},title:{order:100},base:{order:110},meta:{order:200},link:{order:300},script:{order:400},noscript:{order:500},style:{order:600}}),this.tags={html:new Map(null==e?void 0:e.html),body:new Map(null==e?void 0:e.body),meta:new Map(null==e?void 0:e.meta),containers:new Set(null==e?void 0:e.containers)}}setTagsDefinitions(e){this.tagsDefinitions={...this.tagsDefinitions,...e}}static cleanupElementProps(e){const{children:t,dangerouslySetInnerHTML:n,...i}=e;return i}getTags(){const{meta:e,body:t,html:n,containers:i}=this.tags;return{body:t,html:n,meta:new Map([...e.entries()].sort(((e,t)=>{let[,n]=e,[,i]=t;return n.order-i.order}))),containers:i}}replaceAttribute(e,t){return"meta"===e&&"httpequiv"===t.toLowerCase()?"http-equiv":t}createDomElement(e){if(this.isServer)return;const{type:t,props:n}=e,i=document.createElement(t);return this.applyDomElementAttributes(i,n),i}buildKeyByProps(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i="";for(const e of["id","name","href","src"])if(t[e]){i="[".concat(e,"='").concat(t[e],"']");break}if(i||n||(i=Object.entries(t).map((e=>{let[t,n]=e;return"string"==typeof n&&"[".concat(t,"]")})).filter(Boolean).join("")),i)return"".concat(e).concat(i)}getDefaultKey(e,t,n){return"".concat(e,"-").concat(t,"-").concat(n,"-not-unique")}isNotUniqueTag(e){return e.endsWith("-not-unique")}pushElements(e,t){let i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:o.init;n.Children.forEach(e,((e,n)=>{var s,d,u,c,h,m,v;if(!e||"object"!=typeof e||!("type"in e)||!e.type)return;const{type:g,props:f}=e,p=null!=f?f:{};let y=null!==(s=null===(d=this.tagsDefinitions[g])||void 0===d?void 0:d.order)&&void 0!==s?s:1e3,b=null!==(u=null===(c=this.tagsDefinitions[g])||void 0===c?void 0:c.key)&&void 0!==u?u:g,E=e;switch(g){case"title":const{children:o}=p;E=Array.isArray(o)?a.default.cloneElement(e,{children:o.join("")}):e;break;case"meta":const{charSet:r,httpEquiv:s}=p;b=r?"meta[charset]":s?"meta[httpEquiv]":this.buildKeyByProps(g,p);break;case"html":case"body":if(!i&&this.tags[g].has(b))return;return void this.tags[g].set(t,l.cleanupElementProps(p));default:b=null!==(h=this.buildKeyByProps(g,p,!0))&&void 0!==h?h:this.getDefaultKey(g,t,n)}if((i||!this.tags.meta.has(b))&&(!this.isNotUniqueTag(b)||(null===(m=this.tags.meta.get(b))||void 0===m?void 0:m.status)!==o.synced)){var T;if(null!==(v=this.tagsDefinitions[b])&&void 0!==v&&v.order)y=null===(T=this.tagsDefinitions[b])||void 0===T?void 0:T.order;this.tags.meta.set(b,{element:this.isServer?E:void 0,domElement:t===l.rootContainerId?void 0:this.createDomElement(E),order:y,containerId:t,status:r})}})),this.tags.containers.add(t)}getRootTagProps(e){return e.size?[...e.values()].reduce(((e,t)=>({...e,...t})),{}):{}}applyDomElementAttributes(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=e.tagName.toLowerCase();Object.entries(t).forEach((t=>{let[i,o]=t;if("children"!==i)return"style"===i?Object.entries(o).forEach((t=>{let[n,i]=t;e.style[n]=i})):void e.setAttribute(this.replaceAttribute(n,i),o);e.innerHTML=o}))}syncMeta(e){var t;const{meta:n,html:i,body:r}=this.getTags();for(const{name:e,value:t}of[{name:"html",value:i},{name:"body",value:r}]){var s;const n=this.getRootTagProps(t),i=document.querySelector(e);(null!==(s=null==i?void 0:i.getAttributeNames())&&void 0!==s?s:[]).forEach((e=>{n[e]||i.removeAttribute(e)})),Object.keys(n).length&&i&&this.applyDomElementAttributes(i,n)}if(!n.size||this.isServer)return;const a=null===(t=document.getElementsByTagName("head"))||void 0===t?void 0:t[0];let l;n.forEach(((t,i)=>{const{status:r,domElement:s}=t;if(r===o.synced||!s||e&&e!==t.containerId)return void(l=s);if(r===o.drain)return l=s.previousSibling,null==s||s.remove(),void n.delete(i);if(t.status=o.synced,!this.isNotUniqueTag(i)){const e=document.querySelector(i);if(e)return l=s,void e.replaceWith(s)}const d=l&&l.nextSibling;d?a.insertBefore(s,d):a.append(s),l=s}))}pushTags(e,t){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];const i=this.tags.containers.has(t);this.pushElements(e,t,n,i?o.synced:o.init),this.isServer||i||this.syncMeta()}removeTags(e){this.tags.meta.forEach(((t,n)=>{if(t.containerId===e&&t.containerId!==l.rootContainerId){var i;if(this.isNotUniqueTag(n))return null===(i=t.domElement)||void 0===i||i.remove(),void this.tags.meta.delete(n);t.status=o.drain}})),e!==l.rootContainerId&&(this.tags.body.delete(e),this.tags.html.delete(e),this.tags.containers.delete(e))}analyzeClientHead(){var e,t;if(this.isServer)return;for(const e of["html","body"]){var n,i;const t=null===(n=document.getElementsByTagName(e))||void 0===n||null===(i=n[0].cloneNode(!1))||void 0===i?void 0:i.outerHTML;this.pushElements(s.default(t),l.rootContainerId,!1)}const r=null===(e=document.getElementsByTagName("head"))||void 0===e?void 0:e[0],a=s.default(null!==(t=null==r?void 0:r.innerHTML)&&void 0!==t?t:"");this.pushElements(a,l.rootContainerId,!1,o.synced);const d=[...this.tags.meta.values()];r.childNodes.forEach(((e,t)=>{const n=d[t];n&&(n.domElement=e)}))}}r.default(l,"rootContainerId","root"),module.exports=l;
|
|
2
|
+
//# sourceMappingURL=manager.js.map
|
package/manager.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sources":["../src/manager.ts"],"sourcesContent":["import htmlParser from 'html-react-parser';\nimport type { ReactElement, ReactNode } from 'react';\nimport React, { Children } from 'react';\n\nenum TagStatus {\n init = 'init',\n synced = 'synced',\n drain = 'drain',\n}\n\nexport interface IMetaManagerTags {\n html: Map<string, Record<string, any>>; // containerId => styles\n body: Map<string, Record<string, any>>; // containerId => styles\n meta: Map<\n string,\n {\n element?: ReactElement;\n domElement?: HTMLElement;\n order: number;\n containerId: string;\n status: TagStatus;\n }\n >;\n containers: Set<string>;\n}\n\nexport interface IMetaManagerTagsDefinitions {\n [type: string]: {\n key?: string;\n order: number;\n };\n}\n\n/**\n * Meta tags manager\n */\nclass Manager {\n /**\n * Root container id\n */\n public static rootContainerId = 'root';\n\n /**\n * Detect server side\n */\n public isServer = typeof window === 'undefined';\n\n /**\n * Meta tags state\n */\n protected tags: IMetaManagerTags;\n\n /**\n * Tags definitions\n */\n protected tagsDefinitions: IMetaManagerTagsDefinitions = {\n 'meta[charset]': {\n order: 10,\n },\n \"meta[name='viewport']\": {\n order: 20,\n },\n title: {\n order: 100,\n },\n base: {\n order: 110,\n },\n meta: {\n order: 200,\n },\n link: {\n order: 300,\n },\n script: {\n order: 400,\n },\n noscript: {\n order: 500,\n },\n style: {\n order: 600,\n },\n };\n\n /**\n * @constructor\n */\n constructor(tags: Partial<Manager['tags']> = {}) {\n this.tags = {\n html: new Map(tags?.html),\n body: new Map(tags?.body),\n meta: new Map(tags?.meta),\n containers: new Set(tags?.containers),\n };\n }\n\n /**\n * Set tags definitions\n */\n public setTagsDefinitions(definitions: IMetaManagerTagsDefinitions): void {\n this.tagsDefinitions = { ...this.tagsDefinitions, ...definitions };\n }\n\n /**\n * Remove children, dangerouslySetInnerHTML etc.\n */\n public static cleanupElementProps(props: Record<string, any>): Record<string, any> {\n const { children: _, dangerouslySetInnerHTML: __, ...restProps } = props;\n\n return restProps;\n }\n\n /**\n * Get meta tags\n */\n public getTags(): IMetaManagerTags {\n const { meta, body, html, containers } = this.tags;\n const sortedMeta = new Map(\n [...meta.entries()].sort(([, tagA], [, tagB]) => tagA.order - tagB.order),\n );\n\n return {\n body,\n html,\n meta: sortedMeta,\n containers,\n };\n }\n\n /**\n * Replace react attribute to valid DOM attribute\n */\n protected replaceAttribute(tagName: string, attribute: string): string {\n if (tagName === 'meta' && attribute.toLowerCase() === 'httpequiv') {\n return 'http-equiv';\n }\n\n return attribute;\n }\n\n /**\n * Create DOM element from React element\n */\n protected createDomElement(element: ReactElement): HTMLElement | undefined {\n if (this.isServer) {\n return;\n }\n\n const { type, props } = element;\n const domElement = document.createElement(type as string);\n\n this.applyDomElementAttributes(domElement, props as Record<string, string>);\n\n return domElement;\n }\n\n /**\n * Make element key by element props\n */\n protected buildKeyByProps(\n type: string,\n props: Record<string, any> = {},\n isSkipProps = false,\n ): string | undefined {\n let key = '';\n\n // try to build unique key by unique props\n for (const uniqueAttr of ['id', 'name', 'href', 'src']) {\n if (props[uniqueAttr]) {\n key = `[${uniqueAttr}='${props[uniqueAttr] as string}']`;\n\n break;\n }\n }\n\n // try to build by props\n if (!key && !isSkipProps) {\n key = Object.entries(props)\n .map(([k, v]) => {\n if (typeof v === 'string') {\n return `[${k}]`;\n }\n\n return false;\n })\n .filter(Boolean)\n .join('');\n }\n\n if (!key) {\n return undefined;\n }\n\n return `${type}${key}`;\n }\n\n /**\n * Get default tag key for not unique elements\n */\n protected getDefaultKey(type: string, containerId: string, index: number): string {\n return `${type}-${containerId}-${index}-not-unique`;\n }\n\n /**\n * Check if tag is not unique\n */\n protected isNotUniqueTag(key: string): boolean {\n return key.endsWith('-not-unique');\n }\n\n /**\n * Push react elements to meta state\n */\n protected pushElements(\n elements: ReactNode,\n containerId: string,\n isReplace = true,\n status = TagStatus.init,\n ): void {\n Children.forEach(elements, (child, index) => {\n // skip unsupported elements\n if (!child || typeof child !== 'object' || !('type' in child) || !child.type) {\n return;\n }\n\n const { type, props } = child;\n const elementProps: Record<string, any> = props ?? {};\n\n let order = this.tagsDefinitions[type as string]?.order ?? 1000;\n let key = this.tagsDefinitions[type as string]?.key ?? (type as string);\n let element = child;\n\n switch (type) {\n case 'title':\n const { children } = elementProps;\n\n // fix multiple nodes\n element = Array.isArray(children)\n ? React.cloneElement(child, { children: children.join('') })\n : child;\n break;\n\n case 'meta':\n const { charSet, httpEquiv } = elementProps;\n\n if (charSet) {\n key = `meta[charset]`;\n } else if (httpEquiv) {\n key = `meta[httpEquiv]`;\n } else {\n key = this.buildKeyByProps(type, elementProps)!;\n }\n\n break;\n\n case 'html':\n case 'body':\n if (!isReplace && this.tags[type].has(key)) {\n return;\n }\n\n this.tags[type].set(containerId, Manager.cleanupElementProps(elementProps));\n\n return;\n\n case 'link':\n case 'script':\n case 'noscript':\n case 'style':\n default:\n key =\n this.buildKeyByProps(type as string, elementProps, true) ??\n this.getDefaultKey(type as string, containerId, index);\n break;\n }\n\n // skip replace existed meta tag (e.g. for push existed tags from parsed html, @see ServerManager)\n if (!isReplace && this.tags.meta.has(key)) {\n return;\n }\n\n // skip push already existed in head not unique tags\n if (this.isNotUniqueTag(key) && this.tags.meta.get(key)?.status === TagStatus.synced) {\n return;\n }\n\n // custom order\n if (this.tagsDefinitions[key]?.order) {\n order = this.tagsDefinitions[key]?.order;\n }\n\n this.tags.meta.set(key, {\n element: this.isServer ? element : undefined, // keep element only for server render\n domElement:\n /**\n * create DOM element only for client side\n * generate DOM element for root container inside @see this.analyzeClientHead\n */\n containerId === Manager.rootContainerId ? undefined : this.createDomElement(element),\n order,\n containerId,\n status,\n });\n });\n\n this.tags.containers.add(containerId);\n }\n\n /**\n * Get html or body props\n */\n public getRootTagProps(props: Manager['tags']['html']): Record<string, any> {\n if (!props.size) {\n return {};\n }\n\n return [...props.values()].reduce(\n (res, val) => ({\n ...res,\n ...val,\n }),\n {},\n );\n }\n\n /**\n * Apply props to dom element\n */\n protected applyDomElementAttributes(element: Element, props: Record<string, any> = {}): void {\n const tagName = element.tagName.toLowerCase();\n\n // apply attributes\n Object.entries(props).forEach(([name, value]) => {\n if (name === 'children') {\n element.innerHTML = value;\n\n return;\n }\n\n if (name === 'style') {\n return Object.entries(value as Record<string, string>).forEach(\n ([styleName, styleValue]) => {\n element['style'][styleName] = styleValue;\n },\n );\n }\n\n element.setAttribute(this.replaceAttribute(tagName, name), value as string);\n });\n }\n\n /**\n * Synchronize local meta tags with head meta tags\n */\n protected syncMeta(containerId?: string): void {\n const { meta, html, body } = this.getTags();\n\n // apply html, body props\n for (const { name, value } of [\n { name: 'html', value: html },\n { name: 'body', value: body },\n ]) {\n const tagProps = this.getRootTagProps(value);\n const domElement = document.querySelector(name);\n const existAttributes = domElement?.getAttributeNames() ?? [];\n\n // remove drained props\n existAttributes.forEach((attrName) => {\n if (tagProps[attrName]) {\n return;\n }\n\n domElement!.removeAttribute(attrName);\n });\n\n if (!Object.keys(tagProps).length || !domElement) {\n continue;\n }\n\n this.applyDomElementAttributes(domElement, tagProps);\n }\n\n if (!meta.size || this.isServer) {\n return;\n }\n\n const head = document.getElementsByTagName('head')?.[0];\n let prevElement: HTMLElement | ChildNode | null | undefined;\n\n meta.forEach((metaTag, key) => {\n const { status, domElement } = metaTag;\n\n if (\n status === TagStatus.synced ||\n !domElement ||\n (containerId && containerId !== metaTag.containerId)\n ) {\n prevElement = domElement;\n\n return;\n }\n\n // remove tags\n if (status === TagStatus.drain) {\n prevElement = domElement.previousSibling;\n domElement?.remove();\n meta.delete(key);\n\n return;\n }\n\n metaTag.status = TagStatus.synced;\n\n if (!this.isNotUniqueTag(key)) {\n const exist = document.querySelector(key);\n\n if (exist) {\n prevElement = domElement;\n exist.replaceWith(domElement);\n\n return;\n }\n }\n\n const beforeElement = prevElement && prevElement.nextSibling;\n\n if (beforeElement) {\n head.insertBefore(domElement, beforeElement);\n } else {\n head.append(domElement);\n }\n\n prevElement = domElement;\n });\n }\n\n /**\n * Push new meta tags\n */\n public pushTags(elements: ReactNode, containerId: string, isReplace = true): void {\n const isAdded = this.tags.containers.has(containerId);\n\n this.pushElements(\n elements,\n containerId,\n isReplace,\n isAdded ? TagStatus.synced : TagStatus.init,\n );\n\n // skip sync already synced tags\n if (this.isServer || isAdded) {\n return;\n }\n\n this.syncMeta();\n }\n\n /**\n * Remove meta tags\n */\n public removeTags(containerId: string): void {\n this.tags.meta.forEach((metaTag, key) => {\n if (metaTag.containerId !== containerId || metaTag.containerId === Manager.rootContainerId) {\n return;\n }\n\n // remove immediately not unique tags\n if (this.isNotUniqueTag(key)) {\n metaTag.domElement?.remove();\n this.tags.meta.delete(key);\n\n return;\n }\n\n metaTag.status = TagStatus.drain;\n });\n\n if (containerId === Manager.rootContainerId) {\n return;\n }\n\n this.tags.body.delete(containerId);\n this.tags.html.delete(containerId);\n this.tags.containers.delete(containerId);\n }\n\n /**\n * Initial analyze client head meta tags\n */\n public analyzeClientHead(): void {\n if (this.isServer) {\n return;\n }\n\n // parse default attributes for root tags\n for (const tagName of ['html', 'body']) {\n const htmlTag = document.getElementsByTagName(tagName)?.[0].cloneNode(false)?.[\n 'outerHTML'\n ] as string;\n\n this.pushElements(htmlParser(htmlTag), Manager.rootContainerId, false);\n }\n\n // parse default meta tags\n const head = document.getElementsByTagName('head')?.[0];\n const reactElements = htmlParser(head?.innerHTML ?? '');\n\n this.pushElements(reactElements, Manager.rootContainerId, false, TagStatus.synced);\n\n const meta = [...this.tags.meta.values()];\n\n // attach real dom node to virtual tags\n head.childNodes.forEach((node, i) => {\n const existedElem = meta[i];\n\n if (!existedElem) {\n return;\n }\n\n existedElem.domElement = node as HTMLElement;\n });\n }\n}\n\nexport default Manager;\n"],"names":["TagStatus","Manager","constructor","tags","arguments","length","undefined","_defineProperty","this","window","order","title","base","meta","link","script","noscript","style","html","Map","body","containers","Set","setTagsDefinitions","definitions","tagsDefinitions","static","props","children","_","dangerouslySetInnerHTML","__","restProps","getTags","entries","sort","_ref","_ref2","tagA","tagB","replaceAttribute","tagName","attribute","toLowerCase","createDomElement","element","isServer","type","domElement","document","createElement","applyDomElementAttributes","buildKeyByProps","isSkipProps","key","uniqueAttr","concat","Object","map","_ref3","k","v","filter","Boolean","join","getDefaultKey","containerId","index","isNotUniqueTag","endsWith","pushElements","elements","isReplace","status","init","Children","forEach","child","_this$tagsDefinitions","_this$tagsDefinitions2","_this$tagsDefinitions3","_this$tagsDefinitions4","_this$buildKeyByProps","_this$tags$meta$get","_this$tagsDefinitions5","elementProps","Array","isArray","React","cloneElement","charSet","httpEquiv","has","set","cleanupElementProps","get","synced","_this$tagsDefinitions6","rootContainerId","add","getRootTagProps","size","values","reduce","res","val","_ref4","name","value","_ref5","styleName","styleValue","setAttribute","innerHTML","syncMeta","_document$getElements","_domElement$getAttrib","tagProps","querySelector","getAttributeNames","attrName","removeAttribute","keys","head","getElementsByTagName","prevElement","metaTag","drain","previousSibling","remove","delete","exist","replaceWith","beforeElement","nextSibling","insertBefore","append","pushTags","isAdded","removeTags","_metaTag$domElement","analyzeClientHead","_document$getElements4","_head$innerHTML","_document$getElements2","_document$getElements3","htmlTag","cloneNode","htmlParser","reactElements","childNodes","node","i","existedElem"],"mappings":"kMAIKA,wBAAL,SAAKA,GACHA,EAAa,KAAA,OACbA,EAAiB,OAAA,SACjBA,EAAe,MAAA,OAChB,CAJD,CAAKA,IAAAA,EAAS,CAAA,IAgCd,MAAMC,EAoDJC,cAA+C,IAAnCC,EAAAC,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAiC,CAAA,EA9C7CG,EAAAA,QAGkBC,KAAA,WAAkB,oBAAXC,QAEzBF,EAAAA,QAAAC,KAAA,YAAA,GAKAD,EAAA,QAGyDC,KAAA,kBAAA,CACvD,gBAAiB,CACfE,MAAO,IAET,wBAAyB,CACvBA,MAAO,IAETC,MAAO,CACLD,MAAO,KAETE,KAAM,CACJF,MAAO,KAETG,KAAM,CACJH,MAAO,KAETI,KAAM,CACJJ,MAAO,KAETK,OAAQ,CACNL,MAAO,KAETM,SAAU,CACRN,MAAO,KAETO,MAAO,CACLP,MAAO,OAQTF,KAAKL,KAAO,CACVe,KAAM,IAAIC,IAAIhB,aAAAA,EAAAA,EAAMe,MACpBE,KAAM,IAAID,IAAIhB,aAAAA,EAAAA,EAAMiB,MACpBP,KAAM,IAAIM,IAAIhB,aAAAA,EAAAA,EAAMU,MACpBQ,WAAY,IAAIC,IAAInB,aAAI,EAAJA,EAAMkB,YAE9B,CAKOE,mBAAmBC,GACxBhB,KAAKiB,gBAAkB,IAAKjB,KAAKiB,mBAAoBD,EACvD,CAKOE,2BAA2BC,GAChC,MAAQC,SAAUC,EAAGC,wBAAyBC,KAAOC,GAAcL,EAEnE,OAAOK,CACT,CAKOC,UACL,MAAMpB,KAAEA,EAAIO,KAAEA,EAAIF,KAAEA,EAAIG,WAAEA,GAAeb,KAAKL,KAK9C,MAAO,CACLiB,OACAF,OACAL,KAPiB,IAAIM,IACrB,IAAIN,EAAKqB,WAAWC,MAAK,CAAAC,EAAAC,KAAA,IAAIC,CAAAA,GAAKF,GAAKG,CAAAA,GAAKF,EAAA,OAAKC,EAAK5B,MAAQ6B,EAAK7B,KAAK,KAOxEW,aAEJ,CAKUmB,iBAAiBC,EAAiBC,GAC1C,MAAgB,SAAZD,GAAkD,cAA5BC,EAAUC,cAC3B,aAGFD,CACT,CAKUE,iBAAiBC,GACzB,GAAIrC,KAAKsC,SACP,OAGF,MAAMC,KAAEA,EAAIpB,MAAEA,GAAUkB,EAClBG,EAAaC,SAASC,cAAcH,GAI1C,OAFAvC,KAAK2C,0BAA0BH,EAAYrB,GAEpCqB,CACT,CAKUI,gBACRL,GAEmB,IADnBpB,EAAAvB,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAA6B,CAAA,EAC7BiD,EAAWjD,UAAAC,OAAA,QAAAC,IAAAF,UAAA,IAAAA,UAAA,GAEPkD,EAAM,GAGV,IAAK,MAAMC,IAAc,CAAC,KAAM,OAAQ,OAAQ,OAC9C,GAAI5B,EAAM4B,GAAa,CACrBD,EAAG,IAAAE,OAAOD,EAAU,MAAAC,OAAK7B,EAAM4B,GAAyB,MAExD,MAkBJ,GAbKD,GAAQD,IACXC,EAAMG,OAAOvB,QAAQP,GAClB+B,KAAIC,IAAW,IAATC,EAAGC,GAAEF,EACV,MAAiB,iBAANE,GACTL,IAAAA,OAAWI,EAAC,IAGF,IAEbE,OAAOC,SACPC,KAAK,KAGLV,EAIL,MAAA,GAAAE,OAAUT,GAAIS,OAAGF,EACnB,CAKUW,cAAclB,EAAcmB,EAAqBC,GACzD,MAAAX,GAAAA,OAAUT,EAAIS,KAAAA,OAAIU,EAAW,KAAAV,OAAIW,EAAK,cACxC,CAKUC,eAAed,GACvB,OAAOA,EAAIe,SAAS,cACtB,CAKUC,aACRC,EACAL,GAEuB,IADvBM,IAASpE,UAAAC,OAAA,QAAAC,IAAAF,UAAA,KAAAA,UAAA,GACTqE,EAAMrE,UAAAC,OAAAD,QAAAE,IAAAF,UAAAE,GAAAF,UAAGJ,GAAAA,EAAU0E,KAEnBC,EAAAA,SAASC,QAAQL,GAAU,CAACM,EAAOV,KAAS,IAAAW,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAE1C,IAAKP,GAA0B,iBAAVA,KAAwB,SAAUA,KAAWA,EAAM9B,KACtE,OAGF,MAAMA,KAAEA,EAAIpB,MAAEA,GAAUkD,EAClBQ,EAAoC1D,QAAAA,EAAS,GAEnD,IAAIjB,UAAKoE,EAAuCC,QAAvCA,EAAGvE,KAAKiB,gBAAgBsB,UAArBgC,IAAoCA,OAApCA,EAAAA,EAAsCrE,aAAK,IAAAoE,EAAAA,EAAI,IACvDxB,UAAG0B,EAAuCC,QAAvCA,EAAGzE,KAAKiB,gBAAgBsB,UAArBkC,IAAoCA,OAApCA,EAAAA,EAAsC3B,WAAG,IAAA0B,EAAAA,EAAKjC,EACpDF,EAAUgC,EAEd,OAAQ9B,GACN,IAAK,QACH,MAAMnB,SAAEA,GAAayD,EAGrBxC,EAAUyC,MAAMC,QAAQ3D,GACpB4D,EAAK,QAACC,aAAaZ,EAAO,CAAEjD,SAAUA,EAASoC,KAAK,MACpDa,EACJ,MAEF,IAAK,OACH,MAAMa,QAAEA,EAAOC,UAAEA,GAAcN,EAG7B/B,EADEoC,EACmB,gBACZC,EACc,kBAEjBnF,KAAK4C,gBAAgBL,EAAMsC,GAGnC,MAEF,IAAK,OACL,IAAK,OACH,IAAKb,GAAahE,KAAKL,KAAK4C,GAAM6C,IAAItC,GACpC,OAKF,YAFA9C,KAAKL,KAAK4C,GAAM8C,IAAI3B,EAAajE,EAAQ6F,oBAAoBT,IAQ/D,QACE/B,EAC0D4B,QADvDA,EACD1E,KAAK4C,gBAAgBL,EAAgBsC,GAAc,UAAKH,IAAAA,EAAAA,EACxD1E,KAAKyD,cAAclB,EAAgBmB,EAAaC,GAKtD,IAAKK,IAAahE,KAAKL,KAAKU,KAAK+E,IAAItC,OAKjC9C,KAAK4D,eAAed,KAA+B,QAAvB6B,EAAA3E,KAAKL,KAAKU,KAAKkF,IAAIzC,UAAI,IAAA6B,OAAA,EAAvBA,EAAyBV,UAAWzE,EAAUgG,QAA9E,CAKsC,IAAAC,EAAtC,GAA6B,QAA7Bb,EAAI5E,KAAKiB,gBAAgB6B,UAAI,IAAA8B,GAAzBA,EAA2B1E,MAC7BA,EAAiCuF,QAA5BA,EAAGzF,KAAKiB,gBAAgB6B,UAArB2C,IAAyBA,OAAzBA,EAAAA,EAA2BvF,MAGrCF,KAAKL,KAAKU,KAAKgF,IAAIvC,EAAK,CACtBT,QAASrC,KAAKsC,SAAWD,OAAUvC,EACnC0C,WAKEkB,IAAgBjE,EAAQiG,qBAAkB5F,EAAYE,KAAKoC,iBAAiBC,GAC9EnC,QACAwD,cACAO,WACA,IAGJjE,KAAKL,KAAKkB,WAAW8E,IAAIjC,EAC3B,CAKOkC,gBAAgBzE,GACrB,OAAKA,EAAM0E,KAIJ,IAAI1E,EAAM2E,UAAUC,QACzB,CAACC,EAAKC,KAAS,IACVD,KACAC,KAEL,CAAE,GARK,EAUX,CAKUtD,0BAA0BN,GAAiD,IAA/BlB,EAAAvB,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAA6B,CAAA,EACjF,MAAMqC,EAAUI,EAAQJ,QAAQE,cAGhCc,OAAOvB,QAAQP,GAAOiD,SAAQ8B,IAAkB,IAAhBC,EAAMC,GAAMF,EAC1C,GAAa,aAATC,EAMJ,MAAa,UAATA,EACKlD,OAAOvB,QAAQ0E,GAAiChC,SACrDiC,IAA4B,IAA1BC,EAAWC,GAAWF,EACtBhE,EAAe,MAAEiE,GAAaC,CAAU,SAK9ClE,EAAQmE,aAAaxG,KAAKgC,iBAAiBC,EAASkE,GAAOC,GAbzD/D,EAAQoE,UAAYL,CAaqD,GAE/E,CAKUM,SAAShD,GAAoB,IAAAiD,EACrC,MAAMtG,KAAEA,EAAIK,KAAEA,EAAIE,KAAEA,GAASZ,KAAKyB,UAGlC,IAAK,MAAM0E,KAAEA,EAAIC,MAAEA,IAAW,CAC5B,CAAED,KAAM,OAAQC,MAAO1F,GACvB,CAAEyF,KAAM,OAAQC,MAAOxF,IACtB,CAAA,IAAAgG,EACD,MAAMC,EAAW7G,KAAK4F,gBAAgBQ,GAChC5D,EAAaC,SAASqE,cAAcX,IACa,QAAlCS,EAAGpE,aAAU,EAAVA,EAAYuE,2BAAmBH,IAAAA,EAAAA,EAAI,IAG3CxC,SAAS4C,IACnBH,EAASG,IAIbxE,EAAYyE,gBAAgBD,EAAS,IAGlC/D,OAAOiE,KAAKL,GAAUhH,QAAW2C,GAItCxC,KAAK2C,0BAA0BH,EAAYqE,GAG7C,IAAKxG,EAAKwF,MAAQ7F,KAAKsC,SACrB,OAGF,MAAM6E,UAAIR,EAAGlE,SAAS2E,qBAAqB,eAAO,IAAAT,OAAA,EAArCA,EAAwC,GACrD,IAAIU,EAEJhH,EAAK+D,SAAQ,CAACkD,EAASxE,KACrB,MAAMmB,OAAEA,EAAMzB,WAAEA,GAAe8E,EAE/B,GACErD,IAAWzE,EAAUgG,SACpBhD,GACAkB,GAAeA,IAAgB4D,EAAQ5D,YAIxC,YAFA2D,EAAc7E,GAMhB,GAAIyB,IAAWzE,EAAU+H,MAKvB,OAJAF,EAAc7E,EAAWgF,gBACzBhF,SAAAA,EAAYiF,cACZpH,EAAKqH,OAAO5E,GAOd,GAFAwE,EAAQrD,OAASzE,EAAUgG,QAEtBxF,KAAK4D,eAAed,GAAM,CAC7B,MAAM6E,EAAQlF,SAASqE,cAAchE,GAErC,GAAI6E,EAIF,OAHAN,EAAc7E,OACdmF,EAAMC,YAAYpF,GAMtB,MAAMqF,EAAgBR,GAAeA,EAAYS,YAE7CD,EACFV,EAAKY,aAAavF,EAAYqF,GAE9BV,EAAKa,OAAOxF,GAGd6E,EAAc7E,CAAU,GAE5B,CAKOyF,SAASlE,EAAqBL,GAAqC,IAAhBM,IAASpE,UAAAC,OAAA,QAAAC,IAAAF,UAAA,KAAAA,UAAA,GACjE,MAAMsI,EAAUlI,KAAKL,KAAKkB,WAAWuE,IAAI1B,GAEzC1D,KAAK8D,aACHC,EACAL,EACAM,EACAkE,EAAU1I,EAAUgG,OAAShG,EAAU0E,MAIrClE,KAAKsC,UAAY4F,GAIrBlI,KAAK0G,UACP,CAKOyB,WAAWzE,GAChB1D,KAAKL,KAAKU,KAAK+D,SAAQ,CAACkD,EAASxE,KAC/B,GAAIwE,EAAQ5D,cAAgBA,GAAe4D,EAAQ5D,cAAgBjE,EAAQiG,gBAA3E,CAK8B,IAAA0C,EAA9B,GAAIpI,KAAK4D,eAAed,GAItB,OAHkBsF,QAAlBA,EAAAd,EAAQ9E,kBAAR4F,IAAkBA,GAAlBA,EAAoBX,cACpBzH,KAAKL,KAAKU,KAAKqH,OAAO5E,GAKxBwE,EAAQrD,OAASzE,EAAU+H,MAAK,IAG9B7D,IAAgBjE,EAAQiG,kBAI5B1F,KAAKL,KAAKiB,KAAK8G,OAAOhE,GACtB1D,KAAKL,KAAKe,KAAKgH,OAAOhE,GACtB1D,KAAKL,KAAKkB,WAAW6G,OAAOhE,GAC9B,CAKO2E,oBAAiB,IAAAC,EAAAC,EACtB,GAAIvI,KAAKsC,SACP,OAIF,IAAK,MAAML,IAAW,CAAC,OAAQ,QAAS,CAAA,IAAAuG,EAAAC,EACtC,MAAMC,EAAgDF,QAAzCA,EAAG/F,SAAS2E,qBAAqBnF,cAAQuG,GAAsB,QAAtBC,EAAtCD,EAAyC,GAAGG,WAAU,UAAM,IAAAF,SAA5DA,EACH,UAGbzI,KAAK8D,aAAa8E,UAAWF,GAAUjJ,EAAQiG,iBAAiB,GAIlE,MAAMyB,UAAImB,EAAG7F,SAAS2E,qBAAqB,eAAO,IAAAkB,OAAA,EAArCA,EAAwC,GAC/CO,EAAgBD,EAAAA,QAA0BL,QAAhBA,EAACpB,eAAAA,EAAMV,qBAAS8B,EAAAA,EAAI,IAEpDvI,KAAK8D,aAAa+E,EAAepJ,EAAQiG,iBAAiB,EAAOlG,EAAUgG,QAE3E,MAAMnF,EAAO,IAAIL,KAAKL,KAAKU,KAAKyF,UAGhCqB,EAAK2B,WAAW1E,SAAQ,CAAC2E,EAAMC,KAC7B,MAAMC,EAAc5I,EAAK2I,GAEpBC,IAILA,EAAYzG,WAAauG,EAAmB,GAEhD,EAreAhJ,EAAAA,QADIN,EAAO,kBAIqB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lomray/react-head-manager",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React meta tags manager with SSR and Suspense support.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"js",
|
|
9
|
+
"meta",
|
|
10
|
+
"tags",
|
|
11
|
+
"react",
|
|
12
|
+
"ssr",
|
|
13
|
+
"suspense",
|
|
14
|
+
"head",
|
|
15
|
+
"manager",
|
|
16
|
+
"title"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/Lomray-Software/react-head-manager.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Lomray-Software/react-head-manager/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/Lomray-Software/react-head-manager",
|
|
29
|
+
"author": "Mikhail Yarmaliuk",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "rollup -c",
|
|
33
|
+
"build:watch": "rollup -c -w",
|
|
34
|
+
"release": "npm run build && cd lib && npm publish",
|
|
35
|
+
"lint:check": "eslint --ext \".ts,.tsx\" \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
|
|
36
|
+
"lint:format": "eslint --fix --ext \".ts,.tsx\" \"src/**/*.{ts,tsx,*.ts,*tsx}\"",
|
|
37
|
+
"ts:check": "tsc --project ./tsconfig.json --skipLibCheck --noemit"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@babel/runtime": "^7.19.0",
|
|
41
|
+
"@lomray/consistent-suspense": "^1.2.2",
|
|
42
|
+
"html-react-parser": "^4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@babel/core": "^7.19.3",
|
|
46
|
+
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
47
|
+
"@babel/plugin-transform-runtime": "^7.19.1",
|
|
48
|
+
"@babel/preset-env": "^7.19.4",
|
|
49
|
+
"@babel/preset-react": "^7.18.6",
|
|
50
|
+
"@commitlint/cli": "^17.6.5",
|
|
51
|
+
"@commitlint/config-conventional": "^17.6.5",
|
|
52
|
+
"@lomray/eslint-config": "^3.0.0",
|
|
53
|
+
"@lomray/prettier-config": "^1.2.0",
|
|
54
|
+
"@rollup/plugin-json": "^6.0.0",
|
|
55
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
56
|
+
"@types/react": "^18.2.7",
|
|
57
|
+
"@types/react-dom": "^18.2.6",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
|
59
|
+
"@zerollup/ts-transform-paths": "^1.7.18",
|
|
60
|
+
"eslint": "^8.41.0",
|
|
61
|
+
"eslint-config-prettier": "^8.8.0",
|
|
62
|
+
"eslint-plugin-import": "^2.27.5",
|
|
63
|
+
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
64
|
+
"eslint-plugin-prettier": "^4.2.1",
|
|
65
|
+
"husky": "^8.0.3",
|
|
66
|
+
"i": "^0.3.7",
|
|
67
|
+
"lint-staged": "^13.2.2",
|
|
68
|
+
"npm": "^9.7.2",
|
|
69
|
+
"prettier": "^2.8.8",
|
|
70
|
+
"react": "^18.2.0",
|
|
71
|
+
"rollup": "2.79.1",
|
|
72
|
+
"rollup-plugin-copy": "^3.4.0",
|
|
73
|
+
"rollup-plugin-folder-input": "^1.0.1",
|
|
74
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
75
|
+
"rollup-plugin-ts": "^3.2.0",
|
|
76
|
+
"semantic-release": "^21.0.2",
|
|
77
|
+
"ttypescript": "^1.5.15",
|
|
78
|
+
"typescript": "^4.7.4"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"react": ">=17.0.2",
|
|
82
|
+
"react-dom": ">=17.0.2",
|
|
83
|
+
"tslib": "^2.5.2"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Manager from "../manager.js";
|
|
2
|
+
interface IMetaManagerState {
|
|
3
|
+
html: [string, Record<string, any>][];
|
|
4
|
+
body: [string, Record<string, any>][];
|
|
5
|
+
containers: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Helpers for server side
|
|
9
|
+
*/
|
|
10
|
+
declare class ServerManager {
|
|
11
|
+
/**
|
|
12
|
+
* Parse existing head tags from html and merge with tags received at the render stage (from manager)
|
|
13
|
+
*
|
|
14
|
+
* @return new html with actual meta tags
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Parse existing head tags from html and merge with tags received at the render stage (from manager)
|
|
18
|
+
*
|
|
19
|
+
* @return new html with actual meta tags
|
|
20
|
+
*/
|
|
21
|
+
static inject(htmlStr: string, manager: Manager): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get meta manager state to pass on client
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Get meta manager state to pass on client
|
|
27
|
+
*/
|
|
28
|
+
static getState(manager: Manager): IMetaManagerState;
|
|
29
|
+
}
|
|
30
|
+
export { ServerManager as default };
|
package/server/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("@babel/runtime/helpers/inherits"),t=require("@babel/runtime/helpers/setPrototypeOf"),r=require("html-react-parser"),a=require("react"),n=require("react-dom/server"),o=require("../manager.js");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i=l(e),u=l(t),s=l(r),c=l(a),p=l(n);function d(){d=function(e,t){return new r(e,void 0,t)};var e=RegExp.prototype,t=new WeakMap;function r(e,a,n){var o=new RegExp(e,a);return t.set(o,n||t.get(e)),u.default(o,r.prototype)}function a(e,r){var a=t.get(r);return Object.keys(a).reduce((function(t,r){var n=a[r];if("number"==typeof n)t[r]=e[n];else{for(var o=0;void 0===e[n[o]]&&o+1<n.length;)o++;t[r]=e[n[o]]}return t}),Object.create(null))}return i.default(r,RegExp),r.prototype.exec=function(t){var r=e.exec.call(this,t);if(r){r.groups=a(r,this);var n=r.indices;n&&(n.groups=a(n,this))}return r},r.prototype[Symbol.replace]=function(r,n){if("string"==typeof n){var o=t.get(this);return e[Symbol.replace].call(this,r,n.replace(/\$<([^>]+)>/g,(function(e,t){var r=o[t];return"$"+(Array.isArray(r)?r.join("$"):r)})))}if("function"==typeof n){var l=this;return e[Symbol.replace].call(this,r,(function(){var e=arguments;return"object"!=typeof e[e.length-1]&&(e=[].slice.call(e)).push(a(e,l)),n.apply(this,e)}))}return e[Symbol.replace].call(this,r,n)},d.apply(this,arguments)}module.exports=class{static inject(e,t){var r,a;const n=e.match(d(/<head[^/][\s\S]+?>?([\s\S]+)<\/head>/,{meta:1})),l=e.match(/<html[^>]*?>/),i=e.match(/<body[^>]*?>/),u=s.default(null!==(r=null==n||null===(a=n.groups)||void 0===a?void 0:a.meta.trim())&&void 0!==r?r:""),h=s.default(null!=l&&l[0]?"".concat(null==l?void 0:l[0].trim(),"</html>"):""),f=s.default(null!=i&&i[0]?"".concat(null==i?void 0:i[0].trim(),"</body>"):"");t.pushTags(h,o.rootContainerId,!1),t.pushTags(f,o.rootContainerId,!1),t.pushTags(u,o.rootContainerId,!1);const{meta:m,html:g,body:y}=t.getTags(),v=p.default.renderToString([...m.values()].map((e=>{let{element:t}=e;return t}))),[b]=p.default.renderToString(c.default.createElement("html",t.getRootTagProps(g))).split("</html>"),[S]=p.default.renderToString(c.default.createElement("body",t.getRootTagProps(y))).split("</body>");return e.replace(d(/<head[^/][\s\S]+?>?([\s\S]+)<\/head>/,{meta:1}),"<head>".concat(v,"</head>")).replace(/<html[^>]*?>/,b).replace(/<body[^>]*?>/,S)}static getState(e){const{html:t,body:r,containers:a}=e.getTags();return{html:[...t.entries()],body:[...r.entries()],containers:[...a]}}};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/server/index.ts"],"sourcesContent":["// noinspection HtmlRequiredTitleElement\n\nimport htmlParser from 'html-react-parser';\nimport type { ReactElement } from 'react';\nimport React from 'react';\nimport ReactDOMServer from 'react-dom/server';\nimport Manager from '../manager';\n\ninterface IMetaManagerState {\n html: [string, Record<string, any>][];\n body: [string, Record<string, any>][];\n containers: string[];\n}\n\n/**\n * Helpers for server side\n */\nclass ServerManager {\n /**\n * Parse existing head tags from html and merge with tags received at the render stage (from manager)\n *\n * @return new html with actual meta tags\n */\n public static inject(htmlStr: string, manager: Manager): string {\n const matchedMetaHtml = htmlStr.match(/<head[^/].+?>?(?<meta>.+)<\\/head>/s);\n const matchedHtmlAttr = htmlStr.match(/<html[^>]*?>/s);\n const matchedBodyAttr = htmlStr.match(/<body[^>]*?>/s);\n const rootTags = htmlParser(matchedMetaHtml?.groups?.meta.trim() ?? '');\n const htmlTag = htmlParser(matchedHtmlAttr?.[0] ? `${matchedHtmlAttr?.[0].trim()}</html>` : '');\n const bodyTag = htmlParser(matchedBodyAttr?.[0] ? `${matchedBodyAttr?.[0].trim()}</body>` : '');\n\n // add root html props\n manager.pushTags(htmlTag, Manager.rootContainerId, false);\n // add root body props\n manager.pushTags(bodyTag, Manager.rootContainerId, false);\n // add root meta tags to manager\n manager.pushTags(rootTags, Manager.rootContainerId, false);\n\n const { meta, html, body } = manager.getTags();\n const htmlMeta = ReactDOMServer.renderToString(\n [...meta.values()].map(({ element }) => element) as unknown as ReactElement,\n );\n const [htmlTagWithProps] = ReactDOMServer.renderToString(\n React.createElement('html', manager.getRootTagProps(html)),\n ).split('</html>');\n const [bodyTagWithProps] = ReactDOMServer.renderToString(\n React.createElement('body', manager.getRootTagProps(body)),\n ).split('</body>');\n\n return htmlStr\n .replace(/<head[^/].+?>?(?<meta>.+)<\\/head>/s, `<head>${htmlMeta}</head>`)\n .replace(/<html[^>]*?>/s, htmlTagWithProps)\n .replace(/<body[^>]*?>/s, bodyTagWithProps);\n }\n\n /**\n * Get meta manager state to pass on client\n */\n public static getState(manager: Manager): IMetaManagerState {\n const { html, body, containers } = manager.getTags();\n\n return {\n html: [...html.entries()],\n body: [...body.entries()],\n containers: [...containers],\n };\n }\n}\n\nexport default ServerManager;\n"],"names":["static","htmlStr","manager","_matchedMetaHtml$grou","_matchedMetaHtml$grou2","matchedMetaHtml","match","_wrapRegExp","meta","matchedHtmlAttr","matchedBodyAttr","rootTags","htmlParser","groups","trim","htmlTag","concat","bodyTag","pushTags","Manager","rootContainerId","html","body","getTags","htmlMeta","ReactDOMServer","renderToString","values","map","_ref","element","htmlTagWithProps","React","createElement","getRootTagProps","split","bodyTagWithProps","replace","containers","entries"],"mappings":"m2CAiBA,MAMSA,cAAcC,EAAiBC,GAAgB,IAAAC,EAAAC,EACpD,MAAMC,EAAkBJ,EAAQK,MAAKC,EAAC,uCAAoC,CAAAC,KAAA,KACpEC,EAAkBR,EAAQK,MAAM,gBAChCI,EAAkBT,EAAQK,MAAM,gBAChCK,EAAWC,EAAU,QAAqC,QAArCT,EAACE,SAAuBD,QAARA,EAAfC,EAAiBQ,cAAjBT,IAAuBA,OAAR,EAAfA,EAAyBI,KAAKM,cAAM,IAAAX,EAAAA,EAAI,IAC9DY,EAAUH,EAAAA,QAAWH,SAAAA,EAAkB,GAAEO,GAAAA,OAAMP,aAAAA,EAAAA,EAAkB,GAAGK,OAAkB,WAAA,IACtFG,EAAUL,EAAAA,QAAWF,SAAAA,EAAkB,GAAEM,GAAAA,OAAMN,aAAAA,EAAAA,EAAkB,GAAGI,OAAkB,WAAA,IAG5FZ,EAAQgB,SAASH,EAASI,EAAQC,iBAAiB,GAEnDlB,EAAQgB,SAASD,EAASE,EAAQC,iBAAiB,GAEnDlB,EAAQgB,SAASP,EAAUQ,EAAQC,iBAAiB,GAEpD,MAAMZ,KAAEA,EAAIa,KAAEA,EAAIC,KAAEA,GAASpB,EAAQqB,UAC/BC,EAAWC,UAAeC,eAC9B,IAAIlB,EAAKmB,UAAUC,KAAIC,IAAA,IAACC,QAAEA,GAASD,EAAA,OAAKC,CAAO,MAE1CC,GAAoBN,EAAAA,QAAeC,eACxCM,EAAAA,QAAMC,cAAc,OAAQ/B,EAAQgC,gBAAgBb,KACpDc,MAAM,YACDC,GAAoBX,EAAAA,QAAeC,eACxCM,EAAAA,QAAMC,cAAc,OAAQ/B,EAAQgC,gBAAgBZ,KACpDa,MAAM,WAER,OAAOlC,EACJoC,QAAO9B,EAAC,uCAAoC,CAAAC,KAAA,IAAA,SAAAQ,OAAWQ,cACvDa,QAAQ,eAAiBN,GACzBM,QAAQ,eAAiBD,EAC9B,CAKOpC,gBAAgBE,GACrB,MAAMmB,KAAEA,EAAIC,KAAEA,EAAIgB,WAAEA,GAAepC,EAAQqB,UAE3C,MAAO,CACLF,KAAM,IAAIA,EAAKkB,WACfjB,KAAM,IAAIA,EAAKiB,WACfD,WAAY,IAAIA,GAEpB"}
|