@@ -5,7 +5,9 @@ import template from '@babel/template';
5
5
import syntaxJsx from '@babel/plugin-syntax-jsx' ;
6
6
// @ts -expect-error
7
7
import { addNamed , addNamespace , isModule } from '@babel/helper-module-imports' ;
8
- import { type NodePath } from '@babel/traverse' ;
8
+ import { type NodePath , type Visitor } from '@babel/traverse' ;
9
+ import ResolveType from '@vue/babel-plugin-resolve-type' ;
10
+ import { declare } from '@babel/helper-plugin-utils' ;
9
11
import transformVueJSX from './transform-vue-jsx' ;
10
12
import sugarFragment from './sugar-fragment' ;
11
13
import type { State , VueJSXPluginOptions } from './interface' ;
@@ -31,181 +33,194 @@ const hasJSX = (parentPath: NodePath<t.Program>) => {
31
33
32
34
const JSX_ANNOTATION_REGEX = / \* ? \s * @ j s x \s + ( [ ^ \s ] + ) / ;
33
35
34
- export default ( { types } : typeof BabelCore ) : BabelCore . PluginObj < State > => ( {
35
- name : 'babel-plugin-jsx' ,
36
- inherits : syntaxJsx ,
37
- visitor : {
38
- ...transformVueJSX ,
39
- ...sugarFragment ,
40
- Program : {
41
- enter ( path , state ) {
42
- if ( hasJSX ( path ) ) {
43
- const importNames = [
44
- 'createVNode' ,
45
- 'Fragment' ,
46
- 'resolveComponent' ,
47
- 'withDirectives' ,
48
- 'vShow' ,
49
- 'vModelSelect' ,
50
- 'vModelText' ,
51
- 'vModelCheckbox' ,
52
- 'vModelRadio' ,
53
- 'vModelText' ,
54
- 'vModelDynamic' ,
55
- 'resolveDirective' ,
56
- 'mergeProps' ,
57
- 'createTextVNode' ,
58
- 'isVNode' ,
59
- ] ;
60
- if ( isModule ( path ) ) {
61
- // import { createVNode } from "vue";
62
- const importMap : Record < string , t . Identifier > = { } ;
63
- importNames . forEach ( ( name ) => {
64
- state . set ( name , ( ) => {
65
- if ( importMap [ name ] ) {
66
- return types . cloneNode ( importMap [ name ] ) ;
67
- }
68
- const identifier = addNamed ( path , name , 'vue' , {
69
- ensureLiveReference : true ,
36
+ export default declare < VueJSXPluginOptions , BabelCore . PluginObj < State > > (
37
+ ( api , opt , dirname ) => {
38
+ const { types } = api ;
39
+ let resolveType : BabelCore . PluginObj < BabelCore . PluginPass > | undefined ;
40
+ if ( opt . resolveType !== false ) {
41
+ if ( typeof opt . resolveType === 'boolean' ) opt . resolveType = { } ;
42
+ resolveType = ResolveType ( api , opt . resolveType , dirname ) ;
43
+ }
44
+ return {
45
+ ...( resolveType || { } ) ,
46
+ name : 'babel-plugin-jsx' ,
47
+ inherits : syntaxJsx ,
48
+ visitor : {
49
+ ...( resolveType ?. visitor as Visitor < State > ) ,
50
+ ...transformVueJSX ,
51
+ ...sugarFragment ,
52
+ Program : {
53
+ enter ( path , state ) {
54
+ if ( hasJSX ( path ) ) {
55
+ const importNames = [
56
+ 'createVNode' ,
57
+ 'Fragment' ,
58
+ 'resolveComponent' ,
59
+ 'withDirectives' ,
60
+ 'vShow' ,
61
+ 'vModelSelect' ,
62
+ 'vModelText' ,
63
+ 'vModelCheckbox' ,
64
+ 'vModelRadio' ,
65
+ 'vModelText' ,
66
+ 'vModelDynamic' ,
67
+ 'resolveDirective' ,
68
+ 'mergeProps' ,
69
+ 'createTextVNode' ,
70
+ 'isVNode' ,
71
+ ] ;
72
+ if ( isModule ( path ) ) {
73
+ // import { createVNode } from "vue";
74
+ const importMap : Record < string , t . Identifier > = { } ;
75
+ importNames . forEach ( ( name ) => {
76
+ state . set ( name , ( ) => {
77
+ if ( importMap [ name ] ) {
78
+ return types . cloneNode ( importMap [ name ] ) ;
79
+ }
80
+ const identifier = addNamed ( path , name , 'vue' , {
81
+ ensureLiveReference : true ,
82
+ } ) ;
83
+ importMap [ name ] = identifier ;
84
+ return identifier ;
85
+ } ) ;
70
86
} ) ;
71
- importMap [ name ] = identifier ;
72
- return identifier ;
73
- } ) ;
74
- } ) ;
75
- const { enableObjectSlots = true } = state . opts ;
76
- if ( enableObjectSlots ) {
77
- state . set ( '@vue/babel-plugin-jsx/runtimeIsSlot' , ( ) => {
78
- if ( importMap . runtimeIsSlot ) {
79
- return importMap . runtimeIsSlot ;
80
- }
81
- const { name : isVNodeName } = state . get (
82
- 'isVNode'
83
- ) ( ) as t . Identifier ;
84
- const isSlot = path . scope . generateUidIdentifier ( 'isSlot' ) ;
85
- const ast = template . ast `
86
- function ${ isSlot . name } (s) {
87
- return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${ isVNodeName } (s));
88
- }
89
- ` ;
90
- const lastImport = ( path . get ( 'body' ) as NodePath [ ] )
91
- . filter ( ( p ) => p . isImportDeclaration ( ) )
92
- . pop ( ) ;
93
- if ( lastImport ) {
94
- lastImport . insertAfter ( ast ) ;
95
- }
96
- importMap . runtimeIsSlot = isSlot ;
97
- return isSlot ;
98
- } ) ;
99
- }
100
- } else {
101
- // var _vue = require('vue');
102
- let sourceName : t . Identifier ;
103
- importNames . forEach ( ( name ) => {
104
- state . set ( name , ( ) => {
105
- if ( ! sourceName ) {
106
- sourceName = addNamespace ( path , 'vue' , {
107
- ensureLiveReference : true ,
87
+ const { enableObjectSlots = true } = state . opts ;
88
+ if ( enableObjectSlots ) {
89
+ state . set ( '@vue/babel-plugin-jsx/runtimeIsSlot' , ( ) => {
90
+ if ( importMap . runtimeIsSlot ) {
91
+ return importMap . runtimeIsSlot ;
92
+ }
93
+ const { name : isVNodeName } = state . get (
94
+ 'isVNode'
95
+ ) ( ) as t . Identifier ;
96
+ const isSlot = path . scope . generateUidIdentifier ( 'isSlot' ) ;
97
+ const ast = template . ast `
98
+ function ${ isSlot . name } (s) {
99
+ return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${ isVNodeName } (s));
100
+ }
101
+ ` ;
102
+ const lastImport = ( path . get ( 'body' ) as NodePath [ ] )
103
+ . filter ( ( p ) => p . isImportDeclaration ( ) )
104
+ . pop ( ) ;
105
+ if ( lastImport ) {
106
+ lastImport . insertAfter ( ast ) ;
107
+ }
108
+ importMap . runtimeIsSlot = isSlot ;
109
+ return isSlot ;
108
110
} ) ;
109
111
}
110
- return t . memberExpression ( sourceName , t . identifier ( name ) ) ;
111
- } ) ;
112
- } ) ;
112
+ } else {
113
+ // var _vue = require('vue');
114
+ let sourceName : t . Identifier ;
115
+ importNames . forEach ( ( name ) => {
116
+ state . set ( name , ( ) => {
117
+ if ( ! sourceName ) {
118
+ sourceName = addNamespace ( path , 'vue' , {
119
+ ensureLiveReference : true ,
120
+ } ) ;
121
+ }
122
+ return t . memberExpression ( sourceName , t . identifier ( name ) ) ;
123
+ } ) ;
124
+ } ) ;
113
125
114
- const helpers : Record < string , t . Identifier > = { } ;
126
+ const helpers : Record < string , t . Identifier > = { } ;
115
127
116
- const { enableObjectSlots = true } = state . opts ;
117
- if ( enableObjectSlots ) {
118
- state . set ( '@vue/babel-plugin-jsx/runtimeIsSlot' , ( ) => {
119
- if ( helpers . runtimeIsSlot ) {
120
- return helpers . runtimeIsSlot ;
121
- }
122
- const isSlot = path . scope . generateUidIdentifier ( 'isSlot' ) ;
123
- const { object : objectName } = state . get (
124
- 'isVNode'
125
- ) ( ) as t . MemberExpression ;
126
- const ast = template . ast `
127
- function ${ isSlot . name } (s) {
128
- return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${
129
- ( objectName as t . Identifier ) . name
130
- } .isVNode(s));
131
- }
132
- ` ;
128
+ const { enableObjectSlots = true } = state . opts ;
129
+ if ( enableObjectSlots ) {
130
+ state . set ( '@vue/babel-plugin-jsx/runtimeIsSlot' , ( ) => {
131
+ if ( helpers . runtimeIsSlot ) {
132
+ return helpers . runtimeIsSlot ;
133
+ }
134
+ const isSlot = path . scope . generateUidIdentifier ( 'isSlot' ) ;
135
+ const { object : objectName } = state . get (
136
+ 'isVNode'
137
+ ) ( ) as t . MemberExpression ;
138
+ const ast = template . ast `
139
+ function ${ isSlot . name } (s) {
140
+ return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${
141
+ ( objectName as t . Identifier ) . name
142
+ } .isVNode(s));
143
+ }
144
+ ` ;
133
145
134
- const nodePaths = path . get ( 'body' ) as NodePath [ ] ;
135
- const lastImport = nodePaths
136
- . filter (
137
- ( p ) =>
138
- p . isVariableDeclaration ( ) &&
139
- p . node . declarations . some (
140
- ( d ) => ( d . id as t . Identifier ) ?. name === sourceName . name
146
+ const nodePaths = path . get ( 'body' ) as NodePath [ ] ;
147
+ const lastImport = nodePaths
148
+ . filter (
149
+ ( p ) =>
150
+ p . isVariableDeclaration ( ) &&
151
+ p . node . declarations . some (
152
+ ( d ) =>
153
+ ( d . id as t . Identifier ) ?. name === sourceName . name
154
+ )
141
155
)
142
- )
143
- . pop ( ) ;
144
- if ( lastImport ) {
145
- lastImport . insertAfter ( ast ) ;
156
+ . pop ( ) ;
157
+ if ( lastImport ) {
158
+ lastImport . insertAfter ( ast ) ;
159
+ }
160
+ return isSlot ;
161
+ } ) ;
146
162
}
147
- return isSlot ;
148
- } ) ;
149
- }
150
- }
151
-
152
- const {
153
- opts : { pragma = '' } ,
154
- file,
155
- } = state ;
163
+ }
156
164
157
- if ( pragma ) {
158
- state . set ( 'createVNode' , ( ) => t . identifier ( pragma ) ) ;
159
- }
165
+ const {
166
+ opts : { pragma = '' } ,
167
+ file,
168
+ } = state ;
160
169
161
- if ( file . ast . comments ) {
162
- for ( const comment of file . ast . comments ) {
163
- const jsxMatches = JSX_ANNOTATION_REGEX . exec ( comment . value ) ;
164
- if ( jsxMatches ) {
165
- state . set ( 'createVNode' , ( ) => t . identifier ( jsxMatches [ 1 ] ) ) ;
170
+ if ( pragma ) {
171
+ state . set ( 'createVNode' , ( ) => t . identifier ( pragma ) ) ;
166
172
}
167
- }
168
- }
169
- }
170
- } ,
171
- exit ( path ) {
172
- const body = path . get ( 'body' ) as NodePath [ ] ;
173
- const specifiersMap = new Map < string , t . ImportSpecifier > ( ) ;
174
173
175
- body
176
- . filter (
177
- ( nodePath ) =>
178
- t . isImportDeclaration ( nodePath . node ) &&
179
- nodePath . node . source . value === 'vue'
180
- )
181
- . forEach ( ( nodePath ) => {
182
- const { specifiers } = nodePath . node as t . ImportDeclaration ;
183
- let shouldRemove = false ;
184
- specifiers . forEach ( ( specifier ) => {
185
- if (
186
- ! specifier . loc &&
187
- t . isImportSpecifier ( specifier ) &&
188
- t . isIdentifier ( specifier . imported )
189
- ) {
190
- specifiersMap . set ( specifier . imported . name , specifier ) ;
191
- shouldRemove = true ;
174
+ if ( file . ast . comments ) {
175
+ for ( const comment of file . ast . comments ) {
176
+ const jsxMatches = JSX_ANNOTATION_REGEX . exec ( comment . value ) ;
177
+ if ( jsxMatches ) {
178
+ state . set ( 'createVNode' , ( ) => t . identifier ( jsxMatches [ 1 ] ) ) ;
179
+ }
180
+ }
192
181
}
193
- } ) ;
194
- if ( shouldRemove ) {
195
- nodePath . remove ( ) ;
196
182
}
197
- } ) ;
183
+ } ,
184
+ exit ( path ) {
185
+ const body = path . get ( 'body' ) as NodePath [ ] ;
186
+ const specifiersMap = new Map < string , t . ImportSpecifier > ( ) ;
198
187
199
- const specifiers = [ ...specifiersMap . keys ( ) ] . map (
200
- ( imported ) => specifiersMap . get ( imported ) !
201
- ) ;
202
- if ( specifiers . length ) {
203
- path . unshiftContainer (
204
- 'body' ,
205
- t . importDeclaration ( specifiers , t . stringLiteral ( 'vue' ) )
206
- ) ;
207
- }
188
+ body
189
+ . filter (
190
+ ( nodePath ) =>
191
+ t . isImportDeclaration ( nodePath . node ) &&
192
+ nodePath . node . source . value === 'vue'
193
+ )
194
+ . forEach ( ( nodePath ) => {
195
+ const { specifiers } = nodePath . node as t . ImportDeclaration ;
196
+ let shouldRemove = false ;
197
+ specifiers . forEach ( ( specifier ) => {
198
+ if (
199
+ ! specifier . loc &&
200
+ t . isImportSpecifier ( specifier ) &&
201
+ t . isIdentifier ( specifier . imported )
202
+ ) {
203
+ specifiersMap . set ( specifier . imported . name , specifier ) ;
204
+ shouldRemove = true ;
205
+ }
206
+ } ) ;
207
+ if ( shouldRemove ) {
208
+ nodePath . remove ( ) ;
209
+ }
210
+ } ) ;
211
+
212
+ const specifiers = [ ...specifiersMap . keys ( ) ] . map (
213
+ ( imported ) => specifiersMap . get ( imported ) !
214
+ ) ;
215
+ if ( specifiers . length ) {
216
+ path . unshiftContainer (
217
+ 'body' ,
218
+ t . importDeclaration ( specifiers , t . stringLiteral ( 'vue' ) )
219
+ ) ;
220
+ }
221
+ } ,
222
+ } ,
208
223
} ,
209
- } ,
210
- } ,
211
- } ) ;
224
+ } ;
225
+ }
226
+ ) ;
0 commit comments