@@ -3,17 +3,15 @@ import type {
3
3
CallExpression , ObjectProperty , File , VariableDeclaration , FunctionExpression , BlockStatement ,
4
4
} from '@babel/types'
5
5
import type MagicString from 'magic-string'
6
- import { ParseResult } from '@babel/parser'
6
+ import { parse , ParseResult } from '@babel/parser'
7
+ import traverse from '@babel/traverse'
7
8
import { pascalCase , stringifyComponentImport } from '../utils'
8
9
import type { Context } from '../context'
10
+ import { ResolveResult } from '../transformer'
11
+ import { VueVersion } from '../..'
9
12
10
13
const debug = Debug ( 'unplugin-vue-components:transform:directive' )
11
14
12
- interface ResolveResult {
13
- rawName : string
14
- replace : ( resolved : string ) => void
15
- }
16
-
17
15
/**
18
16
* get Vue 2 render function position
19
17
* @param ast
@@ -31,84 +29,94 @@ const getRenderFnStart = (ast: ParseResult<File>): number => {
31
29
return start + 1
32
30
}
33
31
34
- const resolveVue2 = async ( node : CallExpression , s : MagicString , ast : ParseResult < File > ) : Promise < ResolveResult [ ] > => {
35
- const { callee, arguments : args } = node
36
- if ( callee . type !== 'Identifier' || callee . name !== '_c' || args [ 1 ] . type !== 'ObjectExpression' )
37
- return [ ]
38
-
39
- const directives = args [ 1 ] . properties . find (
40
- ( property ) : property is ObjectProperty =>
41
- property . type === 'ObjectProperty'
42
- && property . key . type === 'Identifier'
43
- && property . key . name === 'directives' ,
44
- ) ?. value
45
- if ( ! directives || directives . type !== 'ArrayExpression' )
46
- return [ ]
47
-
48
- const renderStart = getRenderFnStart ( ast )
32
+ const resolveVue2 = ( code : string , s : MagicString ) : ResolveResult [ ] => {
33
+ const ast = parse ( code , {
34
+ sourceType : 'module' ,
35
+ } )
36
+ const nodes : CallExpression [ ] = [ ]
37
+ traverse ( ast , {
38
+ CallExpression ( path ) {
39
+ nodes . push ( path . node )
40
+ } ,
41
+ } )
49
42
50
43
const results : ResolveResult [ ] = [ ]
51
- for ( const directive of directives . elements ) {
52
- if ( directive ?. type !== 'ObjectExpression' ) continue
53
- const nameNode = directive . properties . find (
54
- ( p ) : p is ObjectProperty =>
55
- p . type === 'ObjectProperty'
56
- && p . key . type === 'Identifier'
57
- && p . key . name === 'name' ,
44
+ for ( const node of nodes ) {
45
+ const { callee, arguments : args } = node
46
+ // _c(_, {})
47
+ if ( callee . type !== 'Identifier' || callee . name !== '_c' || args [ 1 ] . type !== 'ObjectExpression' )
48
+ continue
49
+
50
+ // { directives: [] }
51
+ const directives = args [ 1 ] . properties . find (
52
+ ( property ) : property is ObjectProperty =>
53
+ property . type === 'ObjectProperty'
54
+ && property . key . type === 'Identifier'
55
+ && property . key . name === 'directives' ,
58
56
) ?. value
59
- if ( nameNode ?. type !== 'StringLiteral' ) continue
60
- const name = nameNode . value
61
- if ( ! name || name . startsWith ( '_' ) ) continue
62
- results . push ( {
63
- rawName : name ,
64
- replace : ( resolved ) => {
65
- s . prependLeft ( renderStart ! , `\nthis.$options.directives["${ name } "] = ${ resolved } ;` )
66
- } ,
67
- } )
57
+ if ( ! directives || directives . type !== 'ArrayExpression' )
58
+ continue
59
+
60
+ const renderStart = getRenderFnStart ( ast )
61
+
62
+ for ( const directive of directives . elements ) {
63
+ if ( directive ?. type !== 'ObjectExpression' ) continue
64
+
65
+ const nameNode = directive . properties . find (
66
+ ( p ) : p is ObjectProperty =>
67
+ p . type === 'ObjectProperty'
68
+ && p . key . type === 'Identifier'
69
+ && p . key . name === 'name' ,
70
+ ) ?. value
71
+ if ( nameNode ?. type !== 'StringLiteral' ) continue
72
+ const name = nameNode . value
73
+ if ( ! name || name . startsWith ( '_' ) ) continue
74
+ results . push ( {
75
+ rawName : name ,
76
+ replace : ( resolved ) => {
77
+ s . prependLeft ( renderStart ! , `\nthis.$options.directives["${ name } "] = ${ resolved } ;` )
78
+ } ,
79
+ } )
80
+ }
68
81
}
69
82
70
83
return results
71
84
}
72
85
73
- const resolveVue3 = async ( node : CallExpression , s : MagicString ) : Promise < ResolveResult [ ] > => {
74
- const { callee, arguments : args } = node
75
- if (
76
- callee . type !== 'Identifier'
77
- || callee . name !== '_resolveDirective'
78
- || args [ 0 ] . type !== 'StringLiteral'
79
- )
80
- return [ ]
86
+ const resolveVue3 = ( code : string , s : MagicString ) : ResolveResult [ ] => {
87
+ const results : ResolveResult [ ] = [ ]
81
88
82
- const rawName = args [ 0 ] . value
83
- if ( ! rawName || rawName . startsWith ( '_' ) ) return [ ]
89
+ for ( const match of code . matchAll ( / _ r e s o l v e D i r e c t i v e \( " ( .+ ?) " \) / g) ) {
90
+ const matchedName = match [ 1 ]
91
+ if ( match . index != null && matchedName && ! matchedName . startsWith ( '_' ) ) {
92
+ const start = match . index
93
+ const end = start + match [ 0 ] . length
94
+ results . push ( {
95
+ rawName : matchedName ,
96
+ replace : resolved => s . overwrite ( start , end , resolved ) ,
97
+ } )
98
+ }
99
+ }
84
100
85
- return [ {
86
- rawName,
87
- replace : resolved => s . overwrite ( node . start ! , node . end ! , resolved ) ,
88
- } ]
101
+ return results
89
102
}
90
103
91
- export default async ( nodes : CallExpression [ ] , version : 'vue2' | 'vue3' , s : MagicString , ctx : Context , sfcPath : string , ast : ParseResult < File > ) => {
104
+ export default async ( code : string , version : VueVersion , s : MagicString , ctx : Context , sfcPath : string ) => {
92
105
let no = 0
93
106
94
- for ( const node of nodes ) {
95
- const results = version === 'vue2' ? await resolveVue2 ( node , s , ast ) : await resolveVue3 ( node , s )
96
- if ( results . length === 0 ) continue
107
+ const results = version === 'vue2' ? resolveVue2 ( code , s ) : resolveVue3 ( code , s )
108
+ for ( const { rawName, replace } of results ) {
109
+ debug ( `| ${ rawName } ` )
110
+ const name = pascalCase ( rawName )
111
+ ctx . updateUsageMap ( sfcPath , [ name ] )
97
112
98
- for ( const result of results ) {
99
- debug ( `| ${ result . rawName } ` )
113
+ const directive = await ctx . findComponent ( name , 'directive' , [ sfcPath ] )
114
+ if ( ! directive ) continue
100
115
101
- const name = pascalCase ( result . rawName )
102
- ctx . updateUsageMap ( sfcPath , [ name ] )
103
- const directive = await ctx . findComponent ( name , 'directive' , [ sfcPath ] )
104
-
105
- if ( ! directive ) continue
106
-
107
- const varName = `__unplugin_directives_${ no } `
108
- s . prepend ( `${ stringifyComponentImport ( { ...directive , name : varName } , ctx ) } ;\n` )
109
- no += 1
110
- result . replace ( varName )
111
- }
116
+ const varName = `__unplugin_directives_${ no } `
117
+ s . prepend ( `${ stringifyComponentImport ( { ...directive , name : varName } , ctx ) } ;\n` )
118
+ no += 1
119
+ replace ( varName )
112
120
}
113
121
114
122
debug ( `^ (${ no } )` )
0 commit comments