11import domElementToReact from "dom-element-to-react" ;
22import * as ReactDOM from "react-dom" ;
33
4+ import ILoadedOptions from "./ILoadedOptions" ;
45import IOptions from "./IOptions" ;
56import IRehydrator from "./IRehydrator" ;
67
78const rehydratableToReactElement = async (
89 el : Element ,
910 rehydrators : IRehydrator ,
10- options : IOptions
11+ options : ILoadedOptions
1112) : Promise < React . ReactElement < any > > => {
12- const rehydratorName = el . getAttribute ( "data-rehydratable" ) ;
13+ const rehydratorSelector = Object . keys ( options . allSelectors ) . find ( selector =>
14+ el . matches ( selector )
15+ ) ;
16+
17+ if ( ! rehydratorSelector ) {
18+ throw new Error ( "No rehydrator selector matched the element." ) ;
19+ }
20+
21+ const rehydratorName = options . allSelectors [ rehydratorSelector ] ;
1322
1423 if ( ! rehydratorName ) {
1524 throw new Error ( "Rehydrator name is missing from element." ) ;
@@ -31,13 +40,13 @@ const rehydratableToReactElement = async (
3140
3241const createCustomHandler = (
3342 rehydrators : IRehydrator ,
34- options : IOptions
43+ options : ILoadedOptions
3544) => async ( node : Node ) => {
3645 // This function will run on _every_ node that domElementToReact encounters.
3746 // Make sure to keep the conditional highly performant.
3847 if (
3948 node . nodeType === Node . ELEMENT_NODE &&
40- ( node as Element ) . hasAttribute ( "data-rehydratable" )
49+ ( node as Element ) . matches ( options . compoundSelector )
4150 ) {
4251 return rehydratableToReactElement ( node as Element , rehydrators , options ) ;
4352 }
@@ -61,7 +70,7 @@ const createReactRoot = (el: Node) => {
6170const rehydrateChildren = async (
6271 el : Node ,
6372 rehydrators : IRehydrator ,
64- options : IOptions
73+ options : ILoadedOptions
6574) => {
6675 const container = createReactRoot ( el ) ;
6776
@@ -91,30 +100,55 @@ const render = ({
91100 ReactDOM . render ( rehydrated as React . ReactElement < any > , root ) ;
92101} ;
93102
94- const createQuerySelector = ( rehydratableIds : string [ ] ) =>
95- rehydratableIds . reduce (
96- ( acc : string , rehydratableId : string ) =>
97- `${ acc ? `${ acc } , ` : "" } [data-rehydratable*="${ rehydratableId } "]` ,
103+ const defaultGetQuerySelector = ( key : string ) =>
104+ `[data-rehydratable*="${ key } "]` ;
105+
106+ const createQuerySelectors = (
107+ rehydratableIds : string [ ] ,
108+ getQuerySelector : ( ( key : string ) => string ) = defaultGetQuerySelector
109+ ) => {
110+ const allSelectors : { [ key : string ] : string } = rehydratableIds . reduce (
111+ ( acc , key ) => ( { ...acc , [ getQuerySelector ( key ) ] : key } ) ,
112+ { }
113+ ) ;
114+
115+ const compoundSelector = Object . keys ( allSelectors ) . reduce (
116+ ( acc : string , selector : string ) => `${ acc ? `${ acc } , ` : "" } ${ selector } ` ,
98117 ""
99118 ) ;
100119
120+ return {
121+ allSelectors,
122+ compoundSelector
123+ } ;
124+ } ;
125+
101126export default async (
102127 container : Element ,
103128 rehydrators : IRehydrator ,
104129 options : IOptions
105130) => {
106- const selector = createQuerySelector ( Object . keys ( rehydrators ) ) ;
107-
108- const roots = Array . from (
109- // TODO: allow setting a container identifier so multiple rehydration instances can exist
110- container . querySelectorAll ( selector )
111- ) . reduce ( ( acc : Element [ ] , root : Element ) => {
112- // filter roots that are contained within other roots
113- if ( ! acc . some ( r => r . contains ( root ) ) ) {
114- acc . push ( root ) ;
115- }
116- return acc ;
117- } , [ ] ) ;
131+ const { allSelectors, compoundSelector } = createQuerySelectors (
132+ Object . keys ( rehydrators ) ,
133+ options . getQuerySelector
134+ ) ;
135+
136+ const loadedOptions : ILoadedOptions = {
137+ allSelectors,
138+ compoundSelector,
139+ extra : options . extra
140+ } ;
141+
142+ const roots = Array . from ( container . querySelectorAll ( compoundSelector ) ) . reduce (
143+ ( acc : Element [ ] , root : Element ) => {
144+ // filter roots that are contained within other roots
145+ if ( ! acc . some ( r => r . contains ( root ) ) ) {
146+ acc . push ( root ) ;
147+ }
148+ return acc ;
149+ } ,
150+ [ ]
151+ ) ;
118152
119153 // TODO: solve race condition when a second rehydrate runs
120154
@@ -128,7 +162,7 @@ export default async (
128162 const {
129163 container : rootContainer ,
130164 rehydrated
131- } = await rehydrateChildren ( root , rehydrators , options ) ;
165+ } = await rehydrateChildren ( root , rehydrators , loadedOptions ) ;
132166
133167 return { root : rootContainer , rehydrated } ;
134168 } catch ( e ) {
0 commit comments