@@ -5,7 +5,9 @@ import template from '@babel/template';
55import syntaxJsx from '@babel/plugin-syntax-jsx' ;
66// @ts -expect-error
77import { 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' ;
911import transformVueJSX from './transform-vue-jsx' ;
1012import sugarFragment from './sugar-fragment' ;
1113import type { State , VueJSXPluginOptions } from './interface' ;
@@ -31,181 +33,194 @@ const hasJSX = (parentPath: NodePath<t.Program>) => {
3133
3234const JSX_ANNOTATION_REGEX = / \* ? \s * @ j s x \s + ( [ ^ \s ] + ) / ;
3335
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+ } ) ;
7086 } ) ;
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 ;
108110 } ) ;
109111 }
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+ } ) ;
113125
114- const helpers : Record < string , t . Identifier > = { } ;
126+ const helpers : Record < string , t . Identifier > = { } ;
115127
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+ ` ;
133145
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+ )
141155 )
142- )
143- . pop ( ) ;
144- if ( lastImport ) {
145- lastImport . insertAfter ( ast ) ;
156+ . pop ( ) ;
157+ if ( lastImport ) {
158+ lastImport . insertAfter ( ast ) ;
159+ }
160+ return isSlot ;
161+ } ) ;
146162 }
147- return isSlot ;
148- } ) ;
149- }
150- }
151-
152- const {
153- opts : { pragma = '' } ,
154- file,
155- } = state ;
163+ }
156164
157- if ( pragma ) {
158- state . set ( 'createVNode' , ( ) => t . identifier ( pragma ) ) ;
159- }
165+ const {
166+ opts : { pragma = '' } ,
167+ file,
168+ } = state ;
160169
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 ) ) ;
166172 }
167- }
168- }
169- }
170- } ,
171- exit ( path ) {
172- const body = path . get ( 'body' ) as NodePath [ ] ;
173- const specifiersMap = new Map < string , t . ImportSpecifier > ( ) ;
174173
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+ }
192181 }
193- } ) ;
194- if ( shouldRemove ) {
195- nodePath . remove ( ) ;
196182 }
197- } ) ;
183+ } ,
184+ exit ( path ) {
185+ const body = path . get ( 'body' ) as NodePath [ ] ;
186+ const specifiersMap = new Map < string , t . ImportSpecifier > ( ) ;
198187
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+ } ,
208223 } ,
209- } ,
210- } ,
211- } ) ;
224+ } ;
225+ }
226+ ) ;
0 commit comments