@@ -4,12 +4,18 @@ import { get_slot_scope } from './shared/get_slot_scope';
4
4
import { boolean_attributes } from './shared/boolean_attributes' ;
5
5
import Renderer , { RenderOptions } from '../Renderer' ;
6
6
import Element from '../../nodes/Element' ;
7
+ import { INode } from '../../nodes/interfaces' ;
7
8
import { x } from 'code-red' ;
8
9
import Expression from '../../nodes/shared/Expression' ;
10
+ import { trim_end , trim_start } from '../../../utils/trim' ;
11
+ import { link } from '../../../utils/link' ;
9
12
10
13
export default function ( node : Element , renderer : Renderer , options : RenderOptions & {
11
14
slot_scopes : Map < any , any > ;
12
15
} ) {
16
+
17
+ const children = remove_whitespace_children ( node . children , node . next ) ;
18
+
13
19
// awkward special case
14
20
let node_contents ;
15
21
@@ -133,7 +139,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
133
139
if ( node_contents !== undefined ) {
134
140
if ( contenteditable ) {
135
141
renderer . push ( ) ;
136
- renderer . render ( node . children , options ) ;
142
+ renderer . render ( children , options ) ;
137
143
const result = renderer . pop ( ) ;
138
144
139
145
renderer . add_expression ( x `($$value => $$value === void 0 ? ${ result } : $$value)(${ node_contents } )` ) ;
@@ -145,7 +151,7 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
145
151
renderer . add_string ( `</${ node . name } >` ) ;
146
152
}
147
153
} else if ( slot && nearest_inline_component ) {
148
- renderer . render ( node . children , options ) ;
154
+ renderer . render ( children , options ) ;
149
155
150
156
if ( ! is_void ( node . name ) ) {
151
157
renderer . add_string ( `</${ node . name } >` ) ;
@@ -163,10 +169,80 @@ export default function(node: Element, renderer: Renderer, options: RenderOption
163
169
output : renderer . pop ( )
164
170
} ) ;
165
171
} else {
166
- renderer . render ( node . children , options ) ;
172
+ renderer . render ( children , options ) ;
167
173
168
174
if ( ! is_void ( node . name ) ) {
169
175
renderer . add_string ( `</${ node . name } >` ) ;
170
176
}
171
177
}
172
178
}
179
+
180
+ // similar logic from `compile/render_dom/wrappers/Fragment`
181
+ // We want to remove trailing whitespace inside an element/component/block,
182
+ // *unless* there is no whitespace between this node and its next sibling
183
+ function remove_whitespace_children ( children : INode [ ] , next ?: INode ) : INode [ ] {
184
+ const nodes : INode [ ] = [ ] ;
185
+ let last_child : INode ;
186
+ let i = children . length ;
187
+ while ( i -- ) {
188
+ const child = children [ i ] ;
189
+
190
+ if ( child . type === 'Text' ) {
191
+ if ( child . should_skip ( ) ) {
192
+ continue ;
193
+ }
194
+
195
+ let { data } = child ;
196
+
197
+ if ( nodes . length === 0 ) {
198
+ const should_trim = next
199
+ ? next . type === 'Text' &&
200
+ / ^ \s / . test ( next . data ) &&
201
+ trimmable_at ( child , next )
202
+ : ! child . has_ancestor ( 'EachBlock' ) ;
203
+
204
+ if ( should_trim ) {
205
+ data = trim_end ( data ) ;
206
+ if ( ! data ) continue ;
207
+ }
208
+ }
209
+
210
+ // glue text nodes (which could e.g. be separated by comments) together
211
+ if ( last_child && last_child . type === 'Text' ) {
212
+ last_child . data = data + last_child . data ;
213
+ continue ;
214
+ }
215
+
216
+ nodes . unshift ( child ) ;
217
+ link ( last_child , last_child = child ) ;
218
+ } else {
219
+ nodes . unshift ( child ) ;
220
+ link ( last_child , last_child = child ) ;
221
+ }
222
+ }
223
+
224
+ const first = nodes [ 0 ] ;
225
+ if ( first && first . type === 'Text' ) {
226
+ first . data = trim_start ( first . data ) ;
227
+ if ( ! first . data ) {
228
+ first . var = null ;
229
+ nodes . shift ( ) ;
230
+
231
+ if ( nodes [ 0 ] ) {
232
+ nodes [ 0 ] . prev = null ;
233
+ }
234
+ }
235
+ }
236
+
237
+ return nodes ;
238
+ }
239
+
240
+ function trimmable_at ( child : INode , next_sibling : INode ) : boolean {
241
+ // Whitespace is trimmable if one of the following is true:
242
+ // The child and its sibling share a common nearest each block (not at an each block boundary)
243
+ // The next sibling's previous node is an each block
244
+ return (
245
+ next_sibling . find_nearest ( / E a c h B l o c k / ) ===
246
+ child . find_nearest ( / E a c h B l o c k / ) || next_sibling . prev . type === 'EachBlock'
247
+ ) ;
248
+ }
0 commit comments