@@ -8,10 +8,10 @@ const MODE_COMMENT = 4;
88const MODE_PROP_SET = 5 ;
99const MODE_PROP_APPEND = 6 ;
1010
11- const TAG_SET = 1 ;
1211const CHILD_APPEND = 0 ;
1312const CHILD_RECURSE = 2 ;
14- const PROPS_ASSIGN = 3 ;
13+ const TAG_SET = 3 ;
14+ const PROPS_ASSIGN = 4 ;
1515const PROP_SET = MODE_PROP_SET ;
1616const PROP_APPEND = MODE_PROP_APPEND ;
1717
@@ -36,30 +36,30 @@ export const treeify = (built, fields) => {
3636 const children = [ ] ;
3737
3838 for ( let i = 1 ; i < built . length ; i ++ ) {
39- const field = built [ i ++ ] ;
40- const value = typeof field === 'number' ? fields [ field - 1 ] : field ;
39+ const type = built [ i ++ ] ;
40+ const value = built [ i ] ? fields [ built [ i ++ ] - 1 ] : built [ ++ i ] ;
4141
42- if ( built [ i ] === TAG_SET ) {
42+ if ( type === TAG_SET ) {
4343 tag = value ;
4444 }
45- else if ( built [ i ] === PROPS_ASSIGN ) {
45+ else if ( type === PROPS_ASSIGN ) {
4646 props . push ( value ) ;
4747 currentProps = null ;
4848 }
49- else if ( built [ i ] === PROP_SET ) {
49+ else if ( type === PROP_SET ) {
5050 if ( ! currentProps ) {
5151 currentProps = Object . create ( null ) ;
5252 props . push ( currentProps ) ;
5353 }
5454 currentProps [ built [ ++ i ] ] = [ value ] ;
5555 }
56- else if ( built [ i ] === PROP_APPEND ) {
56+ else if ( type === PROP_APPEND ) {
5757 currentProps [ built [ ++ i ] ] . push ( value ) ;
5858 }
59- else if ( built [ i ] === CHILD_RECURSE ) {
59+ else if ( type === CHILD_RECURSE ) {
6060 children . push ( _treeify ( value ) ) ;
6161 }
62- else if ( built [ i ] === CHILD_APPEND ) {
62+ else if ( type === CHILD_APPEND ) {
6363 children . push ( value ) ;
6464 }
6565 }
@@ -70,12 +70,20 @@ export const treeify = (built, fields) => {
7070 return children . length > 1 ? children : children [ 0 ] ;
7171} ;
7272
73-
7473export const evaluate = ( h , built , fields , args ) => {
74+ let tmp ;
75+
76+ // `build()` used the first element of the operation list as
77+ // temporary workspace. Now that `build()` is done we can use
78+ // that space to track whether the current element is "dynamic"
79+ // (i.e. it or any of its descendants depend on dynamic values).
80+ built [ 0 ] = 0 ;
81+
7582 for ( let i = 1 ; i < built . length ; i ++ ) {
76- const field = built [ i ] ;
77- const value = typeof field === 'number' ? fields [ field ] : field ;
78- const type = built [ ++ i ] ;
83+ const type = built [ i ++ ] ;
84+
85+ // Set `built[0]` to truthy if this element depends on a dynamic value.
86+ const value = built [ i ] ? fields [ built [ 0 ] = built [ i ++ ] ] : built [ ++ i ] ;
7987
8088 if ( type === TAG_SET ) {
8189 args [ 0 ] = value ;
@@ -90,11 +98,26 @@ export const evaluate = (h, built, fields, args) => {
9098 args [ 1 ] [ built [ ++ i ] ] += ( value + '' ) ;
9199 }
92100 else if ( type ) {
93- // code === CHILD_RECURSE
94- args . push ( h . apply ( null , evaluate ( h , value , fields , [ '' , null ] ) ) ) ;
101+ // type === CHILD_RECURSE
102+ tmp = h . apply ( 0 , evaluate ( h , value , fields , [ '' , null ] ) ) ;
103+ args . push ( tmp ) ;
104+
105+ if ( value [ 0 ] ) {
106+ // If the child element is dynamic, then so is the current element.
107+ built [ 0 ] = 1 ;
108+ }
109+ else {
110+ // Rewrite the operation list in-place if the child element is static.
111+ // The currently evaluated piece `CHILD_RECURSE, 0, [...]` becomes
112+ // `CHILD_APPEND, 0, tmp`.
113+ // Essentially the operation list gets optimized for potential future
114+ // re-evaluations.
115+ built [ i - 2 ] = CHILD_APPEND ;
116+ built [ i ] = tmp ;
117+ }
95118 }
96119 else {
97- // code === CHILD_APPEND
120+ // type === CHILD_APPEND
98121 args . push ( value ) ;
99122 }
100123 }
@@ -118,15 +141,15 @@ export const build = function(statics) {
118141 current . push ( field ? fields [ field ] : buffer ) ;
119142 }
120143 else {
121- current . push ( field || buffer , CHILD_APPEND ) ;
144+ current . push ( CHILD_APPEND , field , buffer ) ;
122145 }
123146 }
124147 else if ( mode === MODE_TAGNAME && ( field || buffer ) ) {
125148 if ( MINI ) {
126149 current [ 1 ] = field ? fields [ field ] : buffer ;
127150 }
128151 else {
129- current . push ( field || buffer , TAG_SET ) ;
152+ current . push ( TAG_SET , field , buffer ) ;
130153 }
131154 mode = MODE_WHITESPACE ;
132155 }
@@ -135,15 +158,15 @@ export const build = function(statics) {
135158 current [ 2 ] = Object . assign ( current [ 2 ] || { } , fields [ field ] ) ;
136159 }
137160 else {
138- current . push ( field , PROPS_ASSIGN ) ;
161+ current . push ( PROPS_ASSIGN , field , 0 ) ;
139162 }
140163 }
141164 else if ( mode === MODE_WHITESPACE && buffer && ! field ) {
142165 if ( MINI ) {
143166 ( current [ 2 ] = current [ 2 ] || { } ) [ buffer ] = true ;
144167 }
145168 else {
146- current . push ( true , PROP_SET , buffer ) ;
169+ current . push ( PROP_SET , 0 , true , buffer ) ;
147170 }
148171 }
149172 else if ( mode >= MODE_PROP_SET ) {
@@ -158,11 +181,11 @@ export const build = function(statics) {
158181 }
159182 else {
160183 if ( buffer || ( ! field && mode === MODE_PROP_SET ) ) {
161- current . push ( buffer , mode , propName ) ;
184+ current . push ( mode , 0 , buffer , propName ) ;
162185 mode = MODE_PROP_APPEND ;
163186 }
164187 if ( field ) {
165- current . push ( field , mode , propName ) ;
188+ current . push ( mode , field , 0 , propName ) ;
166189 mode = MODE_PROP_APPEND ;
167190 }
168191 }
@@ -241,7 +264,7 @@ export const build = function(statics) {
241264 ( current = current [ 0 ] ) . push ( h . apply ( null , mode . slice ( 1 ) ) ) ;
242265 }
243266 else {
244- ( current = current [ 0 ] ) . push ( mode , CHILD_RECURSE ) ;
267+ ( current = current [ 0 ] ) . push ( CHILD_RECURSE , 0 , mode ) ;
245268 }
246269 mode = MODE_SLASH ;
247270 }
0 commit comments