mirror of
				https://github.com/vuejs/babel-plugin-jsx.git
				synced 2025-10-31 09:22:19 +08:00 
			
		
		
		
	fix: throw state.get(...) when using object slots (#494)
				
					
				
			This commit is contained in:
		| @@ -13,7 +13,8 @@ export { VueJSXPluginOptions }; | ||||
| const hasJSX = (parentPath: NodePath<t.Program>) => { | ||||
|   let fileHasJSX = false; | ||||
|   parentPath.traverse({ | ||||
|     JSXElement(path) { // skip ts error | ||||
|     JSXElement(path) { | ||||
|       // skip ts error | ||||
|       fileHasJSX = true; | ||||
|       path.stop(); | ||||
|     }, | ||||
| @@ -62,14 +63,9 @@ export default ({ types }: typeof BabelCore) => ({ | ||||
|                 if (importMap[name]) { | ||||
|                   return types.cloneNode(importMap[name]); | ||||
|                 } | ||||
|                 const identifier = addNamed( | ||||
|                   path, | ||||
|                   name, | ||||
|                   'vue', | ||||
|                   { | ||||
|                     ensureLiveReference: true, | ||||
|                   }, | ||||
|                 ); | ||||
|                 const identifier = addNamed(path, name, 'vue', { | ||||
|                   ensureLiveReference: true, | ||||
|                 }); | ||||
|                 importMap[name] = identifier; | ||||
|                 return identifier; | ||||
|               }); | ||||
| @@ -80,14 +76,18 @@ export default ({ types }: typeof BabelCore) => ({ | ||||
|                 if (importMap.runtimeIsSlot) { | ||||
|                   return importMap.runtimeIsSlot; | ||||
|                 } | ||||
|                 const { name: isVNodeName } = state.get('isVNode')(); | ||||
|                 const { name: isVNodeName } = state.get( | ||||
|                   'isVNode', | ||||
|                 )() as t.Identifier; | ||||
|                 const isSlot = path.scope.generateUidIdentifier('isSlot'); | ||||
|                 const ast = template.ast` | ||||
|                   function ${isSlot.name}(s) { | ||||
|                     return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${isVNodeName}(s)); | ||||
|                   } | ||||
|                 `; | ||||
|                 const lastImport = (path.get('body') as NodePath[]).filter((p) => p.isImportDeclaration()).pop(); | ||||
|                 const lastImport = (path.get('body') as NodePath[]) | ||||
|                   .filter((p) => p.isImportDeclaration()) | ||||
|                   .pop(); | ||||
|                 if (lastImport) { | ||||
|                   lastImport.insertAfter(ast); | ||||
|                 } | ||||
| @@ -97,24 +97,57 @@ export default ({ types }: typeof BabelCore) => ({ | ||||
|             } | ||||
|           } else { | ||||
|             // var _vue = require('vue'); | ||||
|             let sourceName = ''; | ||||
|             let sourceName: t.Identifier; | ||||
|             importNames.forEach((name) => { | ||||
|               state.set(name, () => { | ||||
|                 if (!sourceName) { | ||||
|                   sourceName = addNamespace( | ||||
|                     path, | ||||
|                     'vue', | ||||
|                     { | ||||
|                       ensureLiveReference: true, | ||||
|                     }, | ||||
|                   ).name; | ||||
|                   sourceName = addNamespace(path, 'vue', { | ||||
|                     ensureLiveReference: true, | ||||
|                   }); | ||||
|                 } | ||||
|                 return t.memberExpression(t.identifier(sourceName), t.identifier(name)); | ||||
|                 return t.memberExpression(sourceName, t.identifier(name)); | ||||
|               }); | ||||
|             }); | ||||
|  | ||||
|             const helpers: Record<string, t.Identifier> = {}; | ||||
|  | ||||
|             const { enableObjectSlots = true } = state.opts; | ||||
|             if (enableObjectSlots) { | ||||
|               state.set('@vue/babel-plugin-jsx/runtimeIsSlot', () => { | ||||
|                 if (helpers.runtimeIsSlot) { | ||||
|                   return helpers.runtimeIsSlot; | ||||
|                 } | ||||
|                 const isSlot = path.scope.generateUidIdentifier('isSlot'); | ||||
|                 const { object: objectName } = state.get( | ||||
|                   'isVNode', | ||||
|                 )() as t.MemberExpression; | ||||
|                 const ast = template.ast` | ||||
|                   function ${isSlot.name}(s) { | ||||
|                     return typeof s === 'function' || (Object.prototype.toString.call(s) === '[object Object]' && !${(objectName as t.Identifier).name}.isVNode(s)); | ||||
|                   } | ||||
|                 `; | ||||
|  | ||||
|                 const nodePaths = path.get('body') as NodePath[]; | ||||
|                 const lastImport = nodePaths | ||||
|                   .filter( | ||||
|                     (p) => p.isVariableDeclaration() | ||||
|                       && p.node.declarations.some( | ||||
|                         (d) => (d.id as t.Identifier)?.name === sourceName.name, | ||||
|                       ), | ||||
|                   ) | ||||
|                   .pop(); | ||||
|                 if (lastImport) { | ||||
|                   lastImport.insertAfter(ast); | ||||
|                 } | ||||
|                 return isSlot; | ||||
|               }); | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           const { opts: { pragma = '' }, file } = state; | ||||
|           const { | ||||
|             opts: { pragma = '' }, | ||||
|             file, | ||||
|           } = state; | ||||
|  | ||||
|           if (pragma) { | ||||
|             state.set('createVNode', () => t.identifier(pragma)); | ||||
| @@ -134,13 +167,20 @@ export default ({ types }: typeof BabelCore) => ({ | ||||
|         const body = path.get('body') as NodePath[]; | ||||
|         const specifiersMap = new Map<string, t.ImportSpecifier>(); | ||||
|  | ||||
|         body.filter((nodePath) => t.isImportDeclaration(nodePath.node) | ||||
|           && nodePath.node.source.value === 'vue') | ||||
|         body | ||||
|           .filter( | ||||
|             (nodePath) => t.isImportDeclaration(nodePath.node) | ||||
|               && nodePath.node.source.value === 'vue', | ||||
|           ) | ||||
|           .forEach((nodePath) => { | ||||
|             const { specifiers } = nodePath.node as t.ImportDeclaration; | ||||
|             let shouldRemove = false; | ||||
|             specifiers.forEach((specifier) => { | ||||
|               if (!specifier.loc && t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) { | ||||
|               if ( | ||||
|                 !specifier.loc | ||||
|                 && t.isImportSpecifier(specifier) | ||||
|                 && t.isIdentifier(specifier.imported) | ||||
|               ) { | ||||
|                 specifiersMap.set(specifier.imported.name, specifier); | ||||
|                 shouldRemove = true; | ||||
|               } | ||||
| @@ -154,7 +194,10 @@ export default ({ types }: typeof BabelCore) => ({ | ||||
|           (imported) => specifiersMap.get(imported)!, | ||||
|         ); | ||||
|         if (specifiers.length) { | ||||
|           path.unshiftContainer('body', t.importDeclaration(specifiers, t.stringLiteral('vue'))); | ||||
|           path.unshiftContainer( | ||||
|             'body', | ||||
|             t.importDeclaration(specifiers, t.stringLiteral('vue')), | ||||
|           ); | ||||
|         } | ||||
|       }, | ||||
|     }, | ||||
|   | ||||
| @@ -24,5 +24,3 @@ export interface VueJSXPluginOptions { | ||||
|   /** Replace the function used when compiling JSX expressions */ | ||||
|   pragma?: string; | ||||
| } | ||||
|  | ||||
| export type ExcludesBoolean = <T>(x: T | false | true) => x is T; | ||||
|   | ||||
| @@ -20,10 +20,12 @@ import { | ||||
| import SlotFlags from './slotFlags'; | ||||
| import { PatchFlags } from './patchFlags'; | ||||
| import parseDirectives from './parseDirectives'; | ||||
| import type { State, ExcludesBoolean, Slots } from './interface'; | ||||
| import type { State, Slots } from './interface'; | ||||
|  | ||||
| const xlinkRE = /^xlink([A-Z])/; | ||||
|  | ||||
| type ExcludesBoolean = <T>(x: T | false | true) => x is T; | ||||
|  | ||||
| const getJSXAttributeValue = ( | ||||
|   path: NodePath<t.JSXAttribute>, | ||||
|   state: State, | ||||
|   | ||||
| @@ -4,7 +4,8 @@ | ||||
|     "rootDirs": ["./src"], | ||||
|     "outDir": "dist", | ||||
|     "downlevelIteration": true, | ||||
|     "declaration": true | ||||
|     "declaration": true, | ||||
|     "jsx": "preserve", | ||||
|   }, | ||||
|   "include": ["src/**/*", "global.d.ts"], | ||||
|   "exclude": ["node_modules"] | ||||
|   | ||||
| @@ -11,7 +11,6 @@ | ||||
|     "resolveJsonModule": true, | ||||
|     "esModuleInterop": true, | ||||
|     "removeComments": false, | ||||
|     "jsx": "preserve", | ||||
|     "lib": [ | ||||
|       "esnext", | ||||
|       "dom" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user