@@ -4,6 +4,7 @@ import { asPresentArray, assert, deprecate, isPresentArray } from '@glimmer/util
4
4
import type { SourceLocation , SourcePosition } from '../source/location' ;
5
5
import type * as ASTv1 from './api' ;
6
6
7
+ import { isVoidTag } from '../generation/printer' ;
7
8
import { SYNTHETIC_LOCATION } from '../source/location' ;
8
9
import { Source } from '../source/source' ;
9
10
import { SourceSpan } from '../source/span' ;
@@ -24,7 +25,11 @@ function SOURCE(): Source {
24
25
// Statements
25
26
26
27
export type BuilderHead = string | ASTv1 . CallableExpression ;
27
- export type TagDescriptor = string | { name : string ; selfClosing : boolean } ;
28
+ export type TagDescriptor =
29
+ | string
30
+ | ASTv1 . PathExpression
31
+ | { path : ASTv1 . PathExpression ; selfClosing ?: boolean }
32
+ | { name : string ; selfClosing ?: boolean } ;
28
33
29
34
function buildMustache (
30
35
path : BuilderHead | ASTv1 . Literal ,
@@ -71,7 +76,7 @@ function buildBlock(
71
76
defaultBlock = _defaultBlock ;
72
77
}
73
78
74
- if ( _elseBlock !== undefined && _elseBlock !== null && _elseBlock . type === 'Template' ) {
79
+ if ( _elseBlock ? .type === 'Template' ) {
75
80
deprecate ( `b.program is deprecated. Use b.blockItself instead.` ) ;
76
81
assert ( _elseBlock . locals . length === 0 , '{{else}} block cannot have block params' ) ;
77
82
@@ -177,23 +182,51 @@ export interface BuildElementOptions {
177
182
children ?: ASTv1 . Statement [ ] ;
178
183
comments ?: ASTv1 . MustacheCommentStatement [ ] ;
179
184
blockParams ?: ASTv1 . VarHead [ ] | string [ ] ;
185
+ openTag ?: SourceLocation ;
186
+ closeTag ?: Nullable < SourceLocation > ;
180
187
loc ?: SourceLocation ;
181
188
}
182
189
183
190
function buildElement ( tag : TagDescriptor , options : BuildElementOptions = { } ) : ASTv1 . ElementNode {
184
- let { attrs, blockParams, modifiers, comments, children, loc } = options ;
191
+ let {
192
+ attrs,
193
+ blockParams,
194
+ modifiers,
195
+ comments,
196
+ children,
197
+ openTag,
198
+ closeTag : _closeTag ,
199
+ loc,
200
+ } = options ;
185
201
186
202
// this is used for backwards compat, prior to `selfClosing` being part of the ElementNode AST
187
- let tagName : string ;
188
- let selfClosing = false ;
189
- if ( typeof tag === 'object' ) {
203
+ let path : ASTv1 . PathExpression ;
204
+ let selfClosing : boolean | undefined ;
205
+
206
+ if ( typeof tag === 'string' ) {
207
+ if ( tag . endsWith ( '/' ) ) {
208
+ path = buildPath ( tag . slice ( 0 , - 1 ) ) ;
209
+ selfClosing = true ;
210
+ } else {
211
+ path = buildPath ( tag ) ;
212
+ }
213
+ } else if ( 'type' in tag ) {
214
+ assert ( tag . type === 'PathExpression' , `Invalid tag type ${ tag . type } ` ) ;
215
+ path = tag ;
216
+ } else if ( 'path' in tag ) {
217
+ assert ( tag . path . type === 'PathExpression' , `Invalid tag type ${ tag . path . type } ` ) ;
218
+ path = tag . path ;
190
219
selfClosing = tag . selfClosing ;
191
- tagName = tag . name ;
192
- } else if ( tag . slice ( - 1 ) === '/' ) {
193
- tagName = tag . slice ( 0 , - 1 ) ;
194
- selfClosing = true ;
195
220
} else {
196
- tagName = tag ;
221
+ path = buildPath ( tag . name ) ;
222
+ selfClosing = tag . selfClosing ;
223
+ }
224
+
225
+ if ( selfClosing ) {
226
+ assert (
227
+ _closeTag === null || _closeTag === undefined ,
228
+ 'Cannot build a self-closing tag with a closeTag source location'
229
+ ) ;
197
230
}
198
231
199
232
let params = blockParams ?. map ( ( param ) => {
@@ -204,14 +237,24 @@ function buildElement(tag: TagDescriptor, options: BuildElementOptions = {}): AS
204
237
}
205
238
} ) ;
206
239
240
+ let closeTag : Nullable < SourceSpan > = null ;
241
+
242
+ if ( _closeTag ) {
243
+ closeTag = buildLoc ( _closeTag || null ) ;
244
+ } else if ( _closeTag === undefined ) {
245
+ closeTag = selfClosing || isVoidTag ( path . original ) ? null : buildLoc ( null ) ;
246
+ }
247
+
207
248
return b . element ( {
208
- tag : tagName ,
209
- selfClosing,
249
+ path ,
250
+ selfClosing : selfClosing || false ,
210
251
attributes : attrs || [ ] ,
211
252
params : params || [ ] ,
212
253
modifiers : modifiers || [ ] ,
213
254
comments : comments || [ ] ,
214
255
children : children || [ ] ,
256
+ openTag : buildLoc ( openTag || null ) ,
257
+ closeTag,
215
258
loc : buildLoc ( loc || null ) ,
216
259
} ) ;
217
260
}
@@ -253,7 +296,7 @@ function buildHead(original: string, loc?: SourceLocation): ASTv1.PathExpression
253
296
return b . path ( { head : headNode , tail, loc : buildLoc ( loc || null ) } ) ;
254
297
}
255
298
256
- function buildThis ( loc : SourceLocation ) : ASTv1 . ThisHead {
299
+ function buildThis ( loc ? : SourceLocation ) : ASTv1 . ThisHead {
257
300
return b . this ( { loc : buildLoc ( loc || null ) } ) ;
258
301
}
259
302
@@ -271,8 +314,8 @@ function buildHeadFromString(original: string, loc?: SourceLocation): ASTv1.Path
271
314
272
315
function buildCleanPath (
273
316
head : ASTv1 . PathHead ,
274
- tail : string [ ] ,
275
- loc : SourceLocation
317
+ tail : string [ ] = [ ] ,
318
+ loc ? : SourceLocation
276
319
) : ASTv1 . PathExpression {
277
320
return b . path ( { head, tail, loc : buildLoc ( loc || null ) } ) ;
278
321
}
0 commit comments