mirror of
				https://github.com/vuejs/babel-plugin-jsx.git
				synced 2025-10-31 17:32:32 +08:00 
			
		
		
		
	feat: vSlots
This commit is contained in:
		| @@ -156,7 +156,17 @@ const App = { | |||||||
|  |  | ||||||
| ### 插槽  | ### 插槽  | ||||||
|  |  | ||||||
| 目前功能没有想好怎么实现,欢迎在 issue 中讨论,可以先使用 `props` 来代替 | ```jsx | ||||||
|  | const App = { | ||||||
|  |   setup() { | ||||||
|  |     const slots = { | ||||||
|  |       a: () => <div>A</div>, | ||||||
|  |       b: () => <span>B</span> | ||||||
|  |     } | ||||||
|  |     return () => <A vSlots={slots} /> | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ## 谁在用 | ## 谁在用 | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -157,7 +157,17 @@ const App = { | |||||||
|  |  | ||||||
| ### Slot  | ### Slot  | ||||||
|  |  | ||||||
| Why Not props ? | ```jsx | ||||||
|  | const App = { | ||||||
|  |   setup() { | ||||||
|  |     const slots = { | ||||||
|  |       a: () => <div>A</div>, | ||||||
|  |       b: () => <span>B</span> | ||||||
|  |     } | ||||||
|  |     return () => <A vSlots={slots} /> | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ## Who is using | ## Who is using | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ interface Opts { | |||||||
|   compatibleProps?: boolean; |   compatibleProps?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export type ExcludesFalse = <T>(x: T | false) => x is T; | export type ExcludesBoolean = <T>(x: T | false | true) => x is T; | ||||||
|  |  | ||||||
| export default () => ({ | export default () => ({ | ||||||
|   name: 'babel-plugin-jsx', |   name: 'babel-plugin-jsx', | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import { | |||||||
|   isFragment, |   isFragment, | ||||||
| } from './utils'; | } from './utils'; | ||||||
| import { PatchFlags, PatchFlagNames } from './patchFlags'; | import { PatchFlags, PatchFlagNames } from './patchFlags'; | ||||||
| import { State, ExcludesFalse } from './'; | import { State, ExcludesBoolean } from './'; | ||||||
|  |  | ||||||
| const xlinkRE = /^xlink([A-Z])/; | const xlinkRE = /^xlink([A-Z])/; | ||||||
| const onRE = /^on[^a-z]/; | const onRE = /^on[^a-z]/; | ||||||
| @@ -116,6 +116,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|   const directives: t.ArrayExpression[] = []; |   const directives: t.ArrayExpression[] = []; | ||||||
|   const dynamicPropNames = new Set(); |   const dynamicPropNames = new Set(); | ||||||
|  |  | ||||||
|  |   let slots: t.Identifier | t.Expression | null = null; | ||||||
|   let patchFlag = 0; |   let patchFlag = 0; | ||||||
|  |  | ||||||
|   if (isFragment(path.get('openingElement').get('name'))) { |   if (isFragment(path.get('openingElement').get('name'))) { | ||||||
| @@ -126,6 +127,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|     return { |     return { | ||||||
|       tag, |       tag, | ||||||
|       isComponent, |       isComponent, | ||||||
|  |       slots, | ||||||
|       props: t.nullLiteral(), |       props: t.nullLiteral(), | ||||||
|       directives, |       directives, | ||||||
|       patchFlag, |       patchFlag, | ||||||
| @@ -201,7 +203,10 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|             value: attributeValue, |             value: attributeValue, | ||||||
|           }); |           }); | ||||||
|  |  | ||||||
|           if (directive) { |           if (directiveName === 'slots') { | ||||||
|  |             slots = attributeValue; | ||||||
|  |             return; | ||||||
|  |           } else if (directive) { | ||||||
|             directives.push(t.arrayExpression(directive)); |             directives.push(t.arrayExpression(directive)); | ||||||
|           } else { |           } else { | ||||||
|             // must be v-model and is a component |             // must be v-model and is a component | ||||||
| @@ -302,7 +307,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|         [ |         [ | ||||||
|           ...exps, |           ...exps, | ||||||
|           !!objectProperties.length && t.objectExpression(objectProperties), |           !!objectProperties.length && t.objectExpression(objectProperties), | ||||||
|         ].filter(Boolean as any as ExcludesFalse), |         ].filter(Boolean as any as ExcludesBoolean), | ||||||
|       ); |       ); | ||||||
|     } else { |     } else { | ||||||
|       // single no need for a mergeProps call |       // single no need for a mergeProps call | ||||||
| @@ -316,6 +321,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|     tag, |     tag, | ||||||
|     props: propsExpression, |     props: propsExpression, | ||||||
|     isComponent, |     isComponent, | ||||||
|  |     slots, | ||||||
|     directives, |     directives, | ||||||
|     patchFlag, |     patchFlag, | ||||||
|     dynamicPropNames, |     dynamicPropNames, | ||||||
| @@ -377,6 +383,7 @@ const transformJSXElement = ( | |||||||
|     directives, |     directives, | ||||||
|     patchFlag, |     patchFlag, | ||||||
|     dynamicPropNames, |     dynamicPropNames, | ||||||
|  |     slots | ||||||
|   } = buildProps(path, state); |   } = buildProps(path, state); | ||||||
|  |  | ||||||
|   const { scope: { bindings } } = path; |   const { scope: { bindings } } = path; | ||||||
| @@ -403,18 +410,25 @@ const transformJSXElement = ( | |||||||
|     tag, |     tag, | ||||||
|     // @ts-ignore |     // @ts-ignore | ||||||
|     compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props, |     compatibleProps ? t.callExpression(state.get('compatibleProps'), [props]) : props, | ||||||
|     !!children.length ? ( |     (children.length || slots) ? ( | ||||||
|       isComponent ? t.objectExpression([ |       isComponent && (children.length || slots) | ||||||
|         t.objectProperty( |         ? t.objectExpression([ | ||||||
|           t.identifier('default'), |             !!children.length && t.objectProperty( | ||||||
|           t.arrowFunctionExpression([], t.arrayExpression(children)) |               t.identifier('default'), | ||||||
|         ) |               t.arrowFunctionExpression([], t.arrayExpression(children)) | ||||||
|       ]) : t.arrayExpression(children) |             ), | ||||||
|  |             ...(slots ? ( | ||||||
|  |               t.isObjectExpression(slots) | ||||||
|  |                 ? (slots as any as t.ObjectExpression).properties | ||||||
|  |                 : [t.spreadElement(slots as any)] | ||||||
|  |             ) : []) | ||||||
|  |           ].filter(Boolean as any as ExcludesBoolean)) | ||||||
|  |         : t.arrayExpression(children) | ||||||
|     ) : t.nullLiteral(), |     ) : t.nullLiteral(), | ||||||
|     !!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false), |     !!patchFlag && t.addComment(t.numericLiteral(patchFlag), 'trailing', ` ${flagNames} `, false), | ||||||
|     !!dynamicPropNames.size |     !!dynamicPropNames.size | ||||||
|     && t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))), |     && t.arrayExpression([...dynamicPropNames.keys()].map((name) => t.stringLiteral(name as string))), | ||||||
|   ].filter(Boolean as any as ExcludesFalse)); |   ].filter(Boolean as any as ExcludesBoolean)); | ||||||
|  |  | ||||||
|   if (!directives.length) { |   if (!directives.length) { | ||||||
|     return createVNode; |     return createVNode; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import * as t from '@babel/types'; | |||||||
| import htmlTags from 'html-tags'; | import htmlTags from 'html-tags'; | ||||||
| import svgTags from 'svg-tags'; | import svgTags from 'svg-tags'; | ||||||
| import { NodePath } from '@babel/traverse'; | import { NodePath } from '@babel/traverse'; | ||||||
| import { State, ExcludesFalse } from './'; | import { State, ExcludesBoolean } from './'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * create Identifier |  * create Identifier | ||||||
| @@ -270,7 +270,7 @@ const parseDirectives = (args: { | |||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|       ), |       ), | ||||||
|     ].filter(Boolean as any as ExcludesFalse) : undefined, |     ].filter(Boolean as any as ExcludesBoolean) : undefined, | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -263,6 +263,47 @@ describe('Transform JSX', () => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | describe('slots', () => { | ||||||
|  |   test('with default', () => { | ||||||
|  |     const A = (_, { slots }) => ( | ||||||
|  |       <> | ||||||
|  |         {slots.default()} | ||||||
|  |         {slots.foo('val')} | ||||||
|  |       </> | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       setup() { | ||||||
|  |         const slots = { | ||||||
|  |           foo: (val) => <div>{val}</div>, | ||||||
|  |         }; | ||||||
|  |         return () => <A vSlots={slots}><span>default</span></A>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     expect(wrapper.html()).toBe('<span>default</span><div>val</div>'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('without default', () => { | ||||||
|  |     const A = (_, { slots }) => ( | ||||||
|  |       <> | ||||||
|  |         {slots.foo('foo')} | ||||||
|  |       </> | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       setup() { | ||||||
|  |         const slots = { | ||||||
|  |           foo: (val) => <div>{val}</div>, | ||||||
|  |         }; | ||||||
|  |         return () => <A vSlots={slots} />; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     expect(wrapper.html()).toBe('<div>foo</div>'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  |  | ||||||
| describe('PatchFlags', () => { | describe('PatchFlags', () => { | ||||||
|   test('static', () => { |   test('static', () => { | ||||||
|     const wrapper = shallowMount({ |     const wrapper = shallowMount({ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user