@@ -33,10 +33,23 @@ const LIST_BUILDER_PROPS = ['padding', 'type', 'list', 'childCount', 'childHeigh
3333
3434module . exports = declare ( ( api ) => {
3535 api . assertVersion ( 7 )
36- const componentImports = new Map ( )
36+ function hasTargetTaroComponent ( state , target ) {
37+ return state . taroComponentImports . has ( target )
38+ }
3739
38- function hasTargetTaroComponent ( target ) {
39- return componentImports . has ( target ) && componentImports . get ( target ) . source === TARO_COMPONENTS
40+ function getComponentLocalName ( state , name , scope ) {
41+ if ( state . taroComponentImports . has ( name ) ) {
42+ return state . taroComponentImports . get ( name ) . localName
43+ }
44+ if ( state . generatedImports . has ( name ) ) {
45+ return state . generatedImports . get ( name ) . localName
46+ }
47+ const programScope = scope . getProgramParent ( )
48+ // 始终生成唯一 ID,避免与用户代码或外部包导入的组件本地名冲突
49+ const newId = programScope . generateUidIdentifier ( name )
50+ const record = { localName : newId . name , source : TARO_COMPONENTS }
51+ state . generatedImports . set ( name , record )
52+ return record . localName
4053 }
4154
4255 function pickAttrs ( attrs , props ) {
@@ -51,78 +64,76 @@ module.exports = declare((api) => {
5164 name : 'plugin:transform-taro-components' ,
5265 visitor : {
5366 Program : {
54- exit ( path ) {
55- if ( [ COMPONENT_LIST , COMPONENT_LIST_ITEM ] . some ( ( component ) => componentImports . has ( component ) ) ) {
56- const taroComponentsImportDeclIndex = path . node . body . findIndex ( ( node ) => {
57- return (
58- api . types . isImportDeclaration ( node ) &&
59- api . types . isStringLiteral ( node . source ) &&
60- node . source . value === TARO_COMPONENTS
61- )
67+ enter ( _path , state ) {
68+ // 每个文件单独维护状态,避免跨文件污染
69+ state . taroComponentImports = new Map ( )
70+ state . generatedImports = new Map ( )
71+ } ,
72+ exit ( path , state ) {
73+ const taroComponentImports = state . taroComponentImports
74+ if ( [ COMPONENT_LIST , COMPONENT_LIST_ITEM ] . some ( ( component ) => taroComponentImports . has ( component ) ) ) {
75+ const collectedSpecifiers = [ ]
76+ const remainingBody = [ ]
77+
78+ path . node . body . forEach ( ( node ) => {
79+ if ( api . types . isImportDeclaration ( node ) && api . types . isStringLiteral ( node . source ) ) {
80+ if ( node . source . value === TARO_COMPONENTS ) {
81+ collectedSpecifiers . push ( ...node . specifiers . filter ( ( specifier ) => api . types . isImportSpecifier ( specifier ) ) )
82+ return
83+ }
84+ }
85+ remainingBody . push ( node )
6286 } )
63- if ( taroComponentsImportDeclIndex === - 1 ) {
64- return
65- }
66- const taroComponentsImportDecl = path . node . body [ taroComponentsImportDeclIndex ]
67- path . node . body . splice ( taroComponentsImportDeclIndex , 1 )
87+
6888 // 重新生成 @tarojs/components 导入声明并插入到路径的开头,排除掉 List、ListItem,添加 ScrollView、ListBuilder、View 到 @tarojs/components 导入声明中
69- if ( taroComponentsImportDecl ) {
70- const { specifiers } = taroComponentsImportDecl
71- // 排除掉 List、ListItem
72- const newSpecifiers = specifiers . filter ( ( specifier ) => {
73- return ! (
74- api . types . isImportSpecifier ( specifier ) &&
75- ( specifier . imported ?. name === COMPONENT_LIST || specifier . imported ?. name === COMPONENT_LIST_ITEM )
76- )
77- } )
78-
79- if ( componentImports . has ( COMPONENT_LIST ) ) {
80- const scrollViewLocalName = componentImports . has ( COMPONENT_SCROLL_VIEW )
81- ? componentImports . get ( COMPONENT_SCROLL_VIEW ) . localName
82- : COMPONENT_SCROLL_VIEW
83-
84- const listBuilderLocalName = componentImports . has ( COMPONENT_LIST_BUILDER )
85- ? componentImports . get ( COMPONENT_LIST_BUILDER ) . localName
86- : COMPONENT_LIST_BUILDER
87-
88- if ( ! newSpecifiers . some ( ( specifier ) => specifier . imported ?. name === scrollViewLocalName ) ) {
89- newSpecifiers . push (
90- api . types . importSpecifier (
91- api . types . identifier ( scrollViewLocalName ) ,
92- api . types . identifier ( scrollViewLocalName )
93- )
94- )
95- }
89+ const baseSpecifiers = collectedSpecifiers . filter ( ( specifier ) => {
90+ return ! (
91+ api . types . isImportSpecifier ( specifier ) &&
92+ ( specifier . imported ?. name === COMPONENT_LIST || specifier . imported ?. name === COMPONENT_LIST_ITEM )
93+ )
94+ } )
9695
97- if ( ! newSpecifiers . some ( ( specifier ) => specifier . imported ?. name === listBuilderLocalName ) ) {
98- newSpecifiers . push (
99- api . types . importSpecifier (
100- api . types . identifier ( listBuilderLocalName ) ,
101- api . types . identifier ( listBuilderLocalName )
102- )
103- )
104- }
105- }
106- if ( componentImports . has ( COMPONENT_LIST_ITEM ) ) {
107- const viewLocalName = componentImports . has ( COMPONENT_VIEW )
108- ? componentImports . get ( COMPONENT_VIEW ) . localName
109- : COMPONENT_VIEW
110-
111- if ( ! newSpecifiers . some ( ( specifier ) => specifier . imported ?. name === viewLocalName ) ) {
112- newSpecifiers . push (
113- api . types . importSpecifier ( api . types . identifier ( viewLocalName ) , api . types . identifier ( viewLocalName ) )
114- )
115- }
96+ const specifierMap = new Map ( )
97+ baseSpecifiers . forEach ( ( specifier ) => {
98+ specifierMap . set ( specifier . local . name , specifier )
99+ } )
100+
101+ // 保证重建的 @tarojs/components 导入不重复本地名,且补齐转换所需组件
102+ const ensureSpecifier = ( localName , importedName ) => {
103+ if ( ! specifierMap . has ( localName ) ) {
104+ specifierMap . set (
105+ localName ,
106+ api . types . importSpecifier ( api . types . identifier ( localName ) , api . types . identifier ( importedName ) )
107+ )
116108 }
109+ }
110+
111+ if ( taroComponentImports . has ( COMPONENT_LIST ) ) {
112+ const scrollViewLocalName = getComponentLocalName ( state , COMPONENT_SCROLL_VIEW , path . scope )
113+ const listBuilderLocalName = getComponentLocalName ( state , COMPONENT_LIST_BUILDER , path . scope )
114+
115+ ensureSpecifier ( scrollViewLocalName , COMPONENT_SCROLL_VIEW )
116+ ensureSpecifier ( listBuilderLocalName , COMPONENT_LIST_BUILDER )
117+ }
117118
118- path . node . body . unshift (
119- api . types . importDeclaration ( newSpecifiers , api . types . stringLiteral ( TARO_COMPONENTS ) )
119+ if ( taroComponentImports . has ( COMPONENT_LIST_ITEM ) ) {
120+ const viewLocalName = getComponentLocalName ( state , COMPONENT_VIEW , path . scope )
121+
122+ ensureSpecifier ( viewLocalName , COMPONENT_VIEW )
123+ }
124+
125+ const nextSpecifiers = Array . from ( specifierMap . values ( ) )
126+ if ( nextSpecifiers . length > 0 ) {
127+ remainingBody . unshift (
128+ api . types . importDeclaration ( nextSpecifiers , api . types . stringLiteral ( TARO_COMPONENTS ) )
120129 )
121130 }
131+
132+ path . node . body = remainingBody
122133 }
123134 } ,
124135 } ,
125- ImportDeclaration ( path ) {
136+ ImportDeclaration ( path , state ) {
126137 const { node } = path
127138 const { source, specifiers } = node
128139 if ( api . types . isStringLiteral ( source ) ) {
@@ -136,44 +147,49 @@ module.exports = declare((api) => {
136147 const local = specifier . local
137148
138149 // 收集组件导入信息
139- componentImports . set ( imported . name , {
140- source : packageName ,
141- importedName : imported . name ,
142- localName : local . name ,
143- } )
150+ if ( packageName === TARO_COMPONENTS && ! state . taroComponentImports . has ( imported . name ) ) {
151+ state . taroComponentImports . set ( imported . name , {
152+ source : packageName ,
153+ importedName : imported . name ,
154+ localName : local . name ,
155+ } )
156+ }
144157 }
145158 } )
146159 }
147160 } ,
148- JSXElement ( path ) {
161+ JSXElement ( path , state ) {
149162 const openingElement = path . node . openingElement
150163 if ( openingElement . name && api . types . isJSXIdentifier ( openingElement . name ) ) {
151164 const props = openingElement . attributes
152165 const children = path . node . children
153166 if (
154- hasTargetTaroComponent ( COMPONENT_LIST ) &&
155- openingElement . name . name === componentImports . get ( COMPONENT_LIST ) . localName
167+ hasTargetTaroComponent ( state , COMPONENT_LIST ) &&
168+ openingElement . name . name === state . taroComponentImports . get ( COMPONENT_LIST ) . localName
156169 ) {
170+ const scrollViewName = getComponentLocalName ( state , COMPONENT_SCROLL_VIEW , path . scope )
171+ const listBuilderName = getComponentLocalName ( state , COMPONENT_LIST_BUILDER , path . scope )
172+
157173 // 创建 ScrollView 开始标签
158174 const scrollViewProps = pickAttrs ( props , SCROLL_VIEW_PROPS )
159175 scrollViewProps . push (
160176 api . types . jsxAttribute ( api . types . jsxIdentifier ( 'type' ) , api . types . stringLiteral ( 'custom' ) )
161177 )
162178 const scrollViewOpening = api . types . jsxOpeningElement (
163- api . types . jsxIdentifier ( COMPONENT_SCROLL_VIEW ) ,
179+ api . types . jsxIdentifier ( scrollViewName ) ,
164180 scrollViewProps ,
165181 false
166182 )
167183 // 创建 ScrollView 闭合标签
168- const scrollViewClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( COMPONENT_SCROLL_VIEW ) )
184+ const scrollViewClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( scrollViewName ) )
169185 // 创建 ListBuilder 开始标签
170186 const listBuilderOpening = api . types . jsxOpeningElement (
171- api . types . jsxIdentifier ( COMPONENT_LIST_BUILDER ) ,
187+ api . types . jsxIdentifier ( listBuilderName ) ,
172188 pickAttrs ( props , LIST_BUILDER_PROPS ) ,
173189 false
174190 )
175191 // 创建 ListBuilder 闭合标签
176- const listBuilderClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( COMPONENT_LIST_BUILDER ) )
192+ const listBuilderClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( listBuilderName ) )
177193
178194 // 创建 ListBuilder 元素,包含原 List 的子元素
179195 const listBuilderElement = api . types . jsxElement ( listBuilderOpening , listBuilderClosing , children , false )
@@ -190,12 +206,13 @@ module.exports = declare((api) => {
190206 }
191207
192208 if (
193- hasTargetTaroComponent ( COMPONENT_LIST_ITEM ) &&
194- openingElement . name . name === componentImports . get ( COMPONENT_LIST_ITEM ) . localName
209+ hasTargetTaroComponent ( state , COMPONENT_LIST_ITEM ) &&
210+ openingElement . name . name === state . taroComponentImports . get ( COMPONENT_LIST_ITEM ) . localName
195211 ) {
196- const viewOpening = api . types . jsxOpeningElement ( api . types . jsxIdentifier ( COMPONENT_VIEW ) , props , false )
212+ const viewName = getComponentLocalName ( state , COMPONENT_VIEW , path . scope )
213+ const viewOpening = api . types . jsxOpeningElement ( api . types . jsxIdentifier ( viewName ) , props , false )
197214
198- const viewClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( COMPONENT_VIEW ) )
215+ const viewClosing = api . types . jsxClosingElement ( api . types . jsxIdentifier ( viewName ) )
199216
200217 const viewElement = api . types . jsxElement ( viewOpening , viewClosing , path . node . children , false )
201218
0 commit comments