@@ -18,8 +18,6 @@ function isTrustedValue(value: any) {
18
18
return value . escaped !== undefined && ! value . escaped ;
19
19
}
20
20
21
- export const THIS = 0 ;
22
-
23
21
export default class TemplateCompiler implements Processor < InputOps > {
24
22
static compile ( ast : AST . Template , source : string , options ?: CompileOptions ) : Template {
25
23
let templateVisitor = new TemplateVisitor ( ) ;
@@ -49,6 +47,12 @@ export default class TemplateCompiler implements Processor<InputOps> {
49
47
private locations : Option < SourceLocation > [ ] = [ ] ;
50
48
private includeMeta = true ;
51
49
50
+ private cursorCount = 0 ;
51
+
52
+ cursor ( ) {
53
+ return `%cursor:${ this . cursorCount ++ } %` ;
54
+ }
55
+
52
56
process (
53
57
actions : Action [ ]
54
58
) : { opcodes : readonly Ops < AllocateSymbolsOps > [ ] ; locations : readonly Option < SourceLocation > [ ] } {
@@ -62,6 +66,7 @@ export default class TemplateCompiler implements Processor<InputOps> {
62
66
}
63
67
64
68
startProgram ( [ program ] : [ AST . Template ] ) {
69
+ this . cursorCount = 0 ;
65
70
this . opcode ( [ 'startProgram' , program ] , program ) ;
66
71
}
67
72
@@ -242,7 +247,12 @@ export default class TemplateCompiler implements Processor<InputOps> {
242
247
}
243
248
244
249
block ( [ action /*, index, count*/ ] : [ AST . BlockStatement ] ) {
245
- this . prepareHelper ( action , 'block' ) ;
250
+ if ( isInElement ( action ) ) {
251
+ this . prepareHelper ( action , 'in-element' ) ;
252
+ } else {
253
+ this . prepareHelper ( action , 'block' ) ;
254
+ }
255
+
246
256
let templateId = this . templateIds . pop ( ) ! ;
247
257
let inverseId = action . inverse === null ? null : this . templateIds . pop ( ) ! ;
248
258
this . expression ( action . path , ExpressionContext . BlockHead , action ) ;
@@ -403,12 +413,12 @@ export default class TemplateCompiler implements Processor<InputOps> {
403
413
this . opcode ( [ 'helper' ] , call ) ;
404
414
}
405
415
406
- prepareHelper ( expr : AST . Call , context : string ) {
416
+ prepareHelper ( expr : AST . Call , context : 'helper' | 'modifier' | 'block' | 'in-element' ) {
407
417
assertIsSimplePath ( expr . path , expr . loc , context ) ;
408
418
409
419
let { params, hash } = expr ;
410
420
411
- this . prepareHash ( hash ) ;
421
+ this . prepareHash ( hash , context ) ;
412
422
this . prepareParams ( params ) ;
413
423
}
414
424
@@ -428,23 +438,51 @@ export default class TemplateCompiler implements Processor<InputOps> {
428
438
this . opcode ( [ 'prepareArray' , params . length ] , null ) ;
429
439
}
430
440
431
- prepareHash ( hash : AST . Hash ) {
441
+ prepareHash ( hash : AST . Hash , context : 'helper' | 'modifier' | 'block' | 'in-element' ) {
432
442
let pairs = hash . pairs ;
443
+ let length = pairs . length ;
433
444
434
- if ( ! pairs . length ) {
435
- this . opcode ( [ 'literal' , null ] , null ) ;
436
- return ;
437
- }
445
+ let isInElement = context === 'in-element' ;
446
+ let hasInsertBefore = false ;
438
447
439
- for ( let i = pairs . length - 1 ; i >= 0 ; i -- ) {
448
+ for ( let i = length - 1 ; i >= 0 ; i -- ) {
440
449
let { key, value } = pairs [ i ] ;
441
450
451
+ if ( isInElement ) {
452
+ if ( key === 'guid' ) {
453
+ throw new SyntaxError (
454
+ `Cannot pass \`guid\` to \`{{#in-element}}\` on line ${ value . loc . start . line } .` ,
455
+ value . loc
456
+ ) ;
457
+ }
458
+
459
+ if ( key === 'insertBefore' ) {
460
+ hasInsertBefore = true ;
461
+ }
462
+ }
463
+
442
464
assert ( this [ value . type ] , `Unimplemented ${ value . type } on TemplateCompiler` ) ;
443
465
this [ value . type ] ( value as any ) ;
444
- this . opcode ( [ 'literal' , key ] , null ) ;
466
+ this . opcode ( [ 'literal' , key ] ) ;
445
467
}
446
468
447
- this . opcode ( [ 'prepareObject' , pairs . length ] , null ) ;
469
+ if ( isInElement ) {
470
+ if ( ! hasInsertBefore ) {
471
+ this . opcode ( [ 'literal' , undefined ] ) ;
472
+ this . opcode ( [ 'literal' , 'insertBefore' ] ) ;
473
+ length ++ ;
474
+ }
475
+
476
+ this . opcode ( [ 'literal' , this . cursor ( ) ] ) ;
477
+ this . opcode ( [ 'literal' , 'guid' ] ) ;
478
+ length ++ ;
479
+ }
480
+
481
+ if ( length === 0 ) {
482
+ this . opcode ( [ 'literal' , null ] ) ;
483
+ } else {
484
+ this . opcode ( [ 'prepareObject' , length ] ) ;
485
+ }
448
486
}
449
487
450
488
prepareAttributeValue ( value : AST . AttrNode [ 'value' ] ) : value is AST . TextNode {
@@ -575,6 +613,12 @@ function isPath(node: AST.Node | AST.PathExpression): node is AST.PathExpression
575
613
return node . type === 'PathExpression' ;
576
614
}
577
615
616
+ function isInElement (
617
+ node : AST . BlockStatement
618
+ ) : node is AST . BlockStatement & { path : AST . PathExpression } {
619
+ return isPath ( node . path ) && node . path . original === 'in-element' ;
620
+ }
621
+
578
622
function destructureDynamicComponent ( element : AST . ElementNode ) : Option < AST . PathExpression > {
579
623
let open = element . tag . charAt ( 0 ) ;
580
624
0 commit comments