1
1
import {
2
2
type SimpleExpressionNode ,
3
3
createSimpleExpression ,
4
+ isStaticNode ,
4
5
walkIdentifiers ,
5
6
} from '@vue/compiler-dom'
6
- import { genBlock } from './block'
7
+ import { genBlockContent } from './block'
7
8
import { genExpression } from './expression'
8
9
import type { CodegenContext } from '../generate'
9
- import type { ForIRNode } from '../ir'
10
- import { type CodeFragment , NEWLINE , genCall , genMulti } from './utils'
11
- import type { Identifier } from '@babel/types'
10
+ import type { BlockIRNode , ForIRNode , IREffect } from '../ir'
11
+ import {
12
+ type CodeFragment ,
13
+ INDENT_END ,
14
+ INDENT_START ,
15
+ NEWLINE ,
16
+ genCall ,
17
+ genMulti ,
18
+ } from './utils'
19
+ import { isNodesEquivalent , type Identifier } from '@babel/types'
12
20
import { parseExpression } from '@babel/parser'
13
21
import { VaporVForFlags } from '../../../shared/src/vaporFlags'
22
+ import { getRuntimeHelper } from './prop'
14
23
15
24
export function genFor (
16
25
oper : ForIRNode ,
@@ -78,7 +87,53 @@ export function genFor(
78
87
idMap [ indexVar ] = null
79
88
}
80
89
81
- const blockFn = context . withId ( ( ) => genBlock ( render , context , args ) , idMap )
90
+ const { selectorPatterns } = matchPatterns ( render , keyProp )
91
+ const patternFrag : CodeFragment [ ] = [ ]
92
+
93
+ for ( let i = 0 ; i < selectorPatterns . length ; i ++ ) {
94
+ const { selector, trueValue, falseValue, effect } = selectorPatterns [ i ]
95
+ const selectorName = `_selector${ i } `
96
+ const args : CodeFragment [ ] = [
97
+ `() => ` ,
98
+ ...genExpression ( selector , context ) ,
99
+ `, (..._args) => {` ,
100
+ ]
101
+ for ( const oper of effect . operations ) {
102
+ const resolvedHelper = getRuntimeHelper (
103
+ oper . tag ,
104
+ oper . prop . key . content ,
105
+ oper . prop . modifier ,
106
+ )
107
+ args . push (
108
+ NEWLINE ,
109
+ ...genCall ( [ helper ( resolvedHelper . name ) , null ] , `_args[0]` , `_args[1]` ) ,
110
+ )
111
+ }
112
+ args . push ( NEWLINE , `}, ` , trueValue , `, ` , falseValue )
113
+ patternFrag . push (
114
+ NEWLINE ,
115
+ `const ${ selectorName } = ` ,
116
+ ...genCall ( helper ( 'useSelectorPattern' ) , args ) ,
117
+ )
118
+ }
119
+
120
+ const blockFn = context . withId ( ( ) => {
121
+ const frag : CodeFragment [ ] = [ ]
122
+ frag . push ( '(' , ...args , ') => {' , INDENT_START )
123
+ for ( let i = 0 ; i < selectorPatterns . length ; i ++ ) {
124
+ frag . push (
125
+ NEWLINE ,
126
+ `_selector${ i } .register(` ,
127
+ ...genExpression ( keyProp ! , context ) ,
128
+ `, ` ,
129
+ itemVar ,
130
+ `)` ,
131
+ )
132
+ }
133
+ frag . push ( ...genBlockContent ( render , context ) )
134
+ frag . push ( INDENT_END , NEWLINE , '}' )
135
+ return frag
136
+ } , idMap )
82
137
exitScope ( )
83
138
84
139
let flags = 0
@@ -93,6 +148,7 @@ export function genFor(
93
148
}
94
149
95
150
return [
151
+ ...patternFrag ,
96
152
NEWLINE ,
97
153
`const n${ id } = ` ,
98
154
...genCall (
@@ -234,3 +290,83 @@ export function genFor(
234
290
return idMap
235
291
}
236
292
}
293
+
294
+ function matchPatterns (
295
+ render : BlockIRNode ,
296
+ keyProp : SimpleExpressionNode | undefined ,
297
+ ) {
298
+ const selectorPatterns : NonNullable <
299
+ ReturnType < typeof matchSelectorPattern >
300
+ > [ ] = [ ]
301
+
302
+ render . effect = render . effect . filter ( effect => {
303
+ if ( keyProp !== undefined ) {
304
+ const pattern = matchSelectorPattern ( effect , keyProp )
305
+ if ( pattern ) {
306
+ selectorPatterns . push ( pattern )
307
+ return false
308
+ }
309
+ }
310
+ return true
311
+ } )
312
+
313
+ return {
314
+ selectorPatterns,
315
+ }
316
+ }
317
+
318
+ function matchSelectorPattern (
319
+ effect : IREffect ,
320
+ keyProp : SimpleExpressionNode ,
321
+ ) :
322
+ | {
323
+ effect : IREffect
324
+ selector : SimpleExpressionNode
325
+ trueValue : string
326
+ falseValue : string
327
+ }
328
+ | undefined {
329
+ // TODO: expressions can be multiple?
330
+ if ( effect . expressions . length === 1 ) {
331
+ const ast = effect . expressions [ 0 ] . ast
332
+ const content = effect . expressions [ 0 ] . content
333
+ if (
334
+ keyProp !== undefined &&
335
+ typeof ast === 'object' &&
336
+ ast ?. type === 'ConditionalExpression' &&
337
+ ast . test . type === 'BinaryExpression' &&
338
+ ast . test . operator === '===' &&
339
+ ast . test . left . type !== 'PrivateName' &&
340
+ isStaticNode ( ast . consequent ) &&
341
+ isStaticNode ( ast . alternate )
342
+ ) {
343
+ const left = ast . test . left
344
+ const right = ast . test . right
345
+ for ( const [ a , b ] of [
346
+ [ left , right ] ,
347
+ [ right , left ] ,
348
+ ] ) {
349
+ if ( isNodesEquivalent ( a , keyProp . ast ) ) {
350
+ return {
351
+ effect,
352
+ trueValue : content . slice (
353
+ ast . consequent . start ! - 1 ,
354
+ ast . consequent . end ! - 1 ,
355
+ ) ,
356
+ falseValue : content . slice (
357
+ ast . alternate . start ! - 1 ,
358
+ ast . alternate . end ! - 1 ,
359
+ ) ,
360
+ selector : {
361
+ content : content . slice ( b . start ! - 1 , b . end ! - 1 ) ,
362
+ ast : b ,
363
+ loc : b . loc as any ,
364
+ isStatic : false ,
365
+ } as any ,
366
+ }
367
+ }
368
+ }
369
+ }
370
+ }
371
+ return
372
+ }
0 commit comments