@@ -20,11 +20,11 @@ enum State {
20
20
class HtmlTransformPatcher {
21
21
private state : State = State . BUFFERING ;
22
22
private buffer = '' ;
23
- private bodyStartIndex = - 1 ;
24
- private bodyTagEndIndex = - 1 ;
23
+ private headInnerIndex = - 1 ;
24
+ private bodyInnerIndex = - 1 ;
25
25
private isHtmlResponse = false ;
26
26
27
- private appendToBody = '' ;
27
+ private bodyPostContent = '' ;
28
28
private response : ServerResponse ;
29
29
private server : ViteDevServer ;
30
30
private request : IncomingMessage ;
@@ -114,12 +114,22 @@ class HtmlTransformPatcher {
114
114
115
115
switch ( this . state ) {
116
116
case State . BUFFERING :
117
- const bodyMatch = this . buffer . match ( / < b o d y [ ^ > ] * > / i) ;
118
- if ( bodyMatch ) {
119
- this . state = State . PROCESSING_HEAD ;
120
- this . bodyStartIndex = this . buffer . indexOf ( bodyMatch [ 0 ] ) ;
121
- this . bodyTagEndIndex = this . bodyStartIndex + bodyMatch [ 0 ] . length ;
122
- this . processingPromise = this . processHead ( ) ;
117
+ // Note that we scan the entire buffer every time, in case the <head> or <body> tags are split across chunks
118
+ if ( this . headInnerIndex === - 1 ) {
119
+ const headMatch = this . buffer . match ( / < h e a d [ ^ > ] * > / i) ;
120
+ if ( headMatch ) {
121
+ const headOuterIndex = this . buffer . indexOf ( headMatch [ 0 ] ) ;
122
+ this . headInnerIndex = headOuterIndex + headMatch [ 0 ] . length ;
123
+ }
124
+ }
125
+ if ( this . headInnerIndex !== - 1 ) {
126
+ const bodyMatch = this . buffer . slice ( this . headInnerIndex ) . match ( / < b o d y [ ^ > ] * > / i) ;
127
+ if ( bodyMatch ) {
128
+ this . state = State . PROCESSING_HEAD ;
129
+ const bodyOuterIndex = this . buffer . indexOf ( bodyMatch [ 0 ] ) ;
130
+ this . bodyInnerIndex = bodyOuterIndex + bodyMatch [ 0 ] . length ;
131
+ this . processingPromise = this . processHead ( ) ;
132
+ }
123
133
}
124
134
break ;
125
135
@@ -139,31 +149,57 @@ class HtmlTransformPatcher {
139
149
140
150
private async processHead ( ) {
141
151
try {
142
- const headPortion = this . buffer . slice ( 0 , this . bodyTagEndIndex ) ;
143
- const fakeHtml = headPortion + '[FAKE_BODY]</body></html>' ;
144
-
152
+ // We can't pass the actual head to vite because it strips some scripts and then the DOM doesn't match the vdom positions
153
+ const fakeHtml = '<html><head>[FAKE_HEAD]</head><body>[FAKE_BODY]</body></html>' ;
145
154
// Let Vite transform the HTML
146
155
const transformedHtml = await this . server . transformIndexHtml (
147
156
this . request . url || '/' ,
148
157
fakeHtml
149
158
) ;
150
159
151
- // Find the [FAKE_BODY] marker in the transformed result
152
- const fakeBodyIndex = transformedHtml . indexOf ( '[FAKE_BODY]' ) ;
153
- const bodyEndIndex = transformedHtml . indexOf ( '</body>' , fakeBodyIndex ) ;
154
- if ( fakeBodyIndex === - 1 || bodyEndIndex === - 1 ) {
160
+ // Extract the pre and post head and body content. For now, ignore attributes added to the tags
161
+ // If attributes are needed later, put them after the : attribute on qwik's tags
162
+ const fakeHeadIndex = transformedHtml . indexOf ( '[FAKE_HEAD]' ) ;
163
+ const fakeHeadCloseIndex = transformedHtml . indexOf ( '</head>' , fakeHeadIndex ) ;
164
+ if ( fakeHeadIndex === - 1 || fakeHeadCloseIndex === - 1 ) {
165
+ throw new Error ( 'Transformed HTML does not contain [FAKE_HEAD]...</head>' ) ;
166
+ }
167
+ const headPreContent = transformedHtml . slice ( '<html><head>' . length , fakeHeadIndex ) ;
168
+ const headPostContent = transformedHtml . slice (
169
+ fakeHeadIndex + '[FAKE_HEAD]' . length ,
170
+ fakeHeadCloseIndex
171
+ ) ;
172
+ const fakeBodyStartIndex = transformedHtml . indexOf ( '<body>' , fakeHeadCloseIndex ) ;
173
+ const fakeBodyIndex = transformedHtml . indexOf ( '[FAKE_BODY]' , fakeBodyStartIndex ) ;
174
+ const fakeBodyEndIndex = transformedHtml . indexOf ( '</body>' , fakeBodyIndex ) ;
175
+ if ( fakeBodyIndex === - 1 || fakeBodyEndIndex === - 1 ) {
155
176
throw new Error ( 'Transformed HTML does not contain [FAKE_BODY]...</body>' ) ;
156
177
}
157
-
158
- // Extract the transformed head and body tags
159
- const transformedHead = transformedHtml . substring ( 0 , fakeBodyIndex ) ;
160
- this . appendToBody = transformedHtml . substring (
178
+ const bodyPreContent = transformedHtml . slice (
179
+ fakeBodyStartIndex + '<body>' . length ,
180
+ fakeBodyIndex
181
+ ) ;
182
+ this . bodyPostContent = transformedHtml . slice (
161
183
fakeBodyIndex + '[FAKE_BODY]' . length ,
162
- bodyEndIndex
184
+ fakeBodyEndIndex
163
185
) ;
164
- this . buffer = transformedHead + this . buffer . slice ( this . bodyTagEndIndex ) ;
186
+ // Now inject the head pre and post content and the body pre into the buffered content
187
+ // Note that the head tag has attributes
188
+ const headCloseIndex = this . buffer . indexOf ( '</head>' , this . headInnerIndex ) ;
189
+ if ( headCloseIndex === - 1 ) {
190
+ throw new Error ( 'Buffered HTML does not contain </head>' ) ;
191
+ }
165
192
166
- if ( this . appendToBody . length > 0 ) {
193
+ this . buffer =
194
+ this . buffer . slice ( 0 , this . headInnerIndex ) +
195
+ headPreContent +
196
+ this . buffer . slice ( this . headInnerIndex , headCloseIndex ) +
197
+ headPostContent +
198
+ this . buffer . slice ( headCloseIndex , this . bodyInnerIndex ) +
199
+ bodyPreContent +
200
+ this . buffer . slice ( this . bodyInnerIndex ) ;
201
+
202
+ if ( this . bodyPostContent . length > 0 ) {
167
203
this . state = State . STREAMING_BODY ;
168
204
this . handleStreamingBodyState ( ) ;
169
205
return ;
@@ -184,7 +220,7 @@ class HtmlTransformPatcher {
184
220
if ( bodyEndMatch ) {
185
221
const bodyEndPos = this . buffer . indexOf ( bodyEndMatch [ 0 ] ) ;
186
222
this . buffer =
187
- this . buffer . slice ( 0 , bodyEndPos ) + this . appendToBody + this . buffer . slice ( bodyEndPos ) ;
223
+ this . buffer . slice ( 0 , bodyEndPos ) + this . bodyPostContent + this . buffer . slice ( bodyEndPos ) ;
188
224
189
225
this . transitionToPassthrough ( ) ;
190
226
return ;
0 commit comments