diff --git a/packages/babel-plugin-jsx/README.md b/packages/babel-plugin-jsx/README.md index ee375d9a..0b497c36 100644 --- a/packages/babel-plugin-jsx/README.md +++ b/packages/babel-plugin-jsx/README.md @@ -243,24 +243,29 @@ h(A, { #### custom directive -Recommended when using string arguments - ```jsx const App = { - directives: { custom: customDirective }, + directives: { custom: vCustom }, setup() { - return () => ; + return () => ; }, }; ``` +Directive names will resolve a variable matching `/v[A-Z]/` first, `options.directives` is only needed to prevent the import from being reported as unused. + +Arguments and modifiers can be added as an array: + ```jsx -const App = { - directives: { custom: customDirective }, - setup() { - return () => ; - }, -}; +// same as v-custom:arg.a.b="val" in a .vue file + +``` + +Or arguments as part of the attribute name: + +```jsx + + ``` ### Slot diff --git a/packages/babel-plugin-jsx/src/parseDirectives.ts b/packages/babel-plugin-jsx/src/parseDirectives.ts index ba23fb09..f526a8a7 100644 --- a/packages/babel-plugin-jsx/src/parseDirectives.ts +++ b/packages/babel-plugin-jsx/src/parseDirectives.ts @@ -1,6 +1,6 @@ import * as t from '@babel/types'; import { type NodePath } from '@babel/traverse'; -import { createIdentifier } from './utils'; +import { camelize, capitalize, createIdentifier } from './utils'; import type { State } from './interface'; export type Tag = @@ -184,11 +184,14 @@ const resolveDirective = ( } return modelToUse; } - const referenceName = - 'v' + directiveName[0].toUpperCase() + directiveName.slice(1); - if (path.scope.references[referenceName]) { - return t.identifier(referenceName); - } + const referenceName = 'v' + capitalize(camelize(directiveName)); + let scope = path.scope; + do { + if (scope.references[referenceName]) { + return t.identifier(referenceName); + } + scope = scope.parent; + } while (scope); return t.callExpression(createIdentifier(state, 'resolveDirective'), [ t.stringLiteral(directiveName), ]); diff --git a/packages/babel-plugin-jsx/src/utils.ts b/packages/babel-plugin-jsx/src/utils.ts index bc64ab58..aeee96dd 100644 --- a/packages/babel-plugin-jsx/src/utils.ts +++ b/packages/babel-plugin-jsx/src/utils.ts @@ -254,6 +254,14 @@ const onRE = /^on[^a-z]/; export const isOn = (key: string) => onRE.test(key); +const camelizeRE = /-(\w)/g; +export const camelize = (str: string): string => { + return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : '')); +}; +export const capitalize = (str: T): Capitalize => { + return (str.charAt(0).toUpperCase() + str.slice(1)) as Capitalize; +}; + const mergeAsArray = ( existing: t.ObjectProperty, incoming: t.ObjectProperty diff --git a/packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap b/packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap index c4c2f986..1e59a664 100644 --- a/packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap +++ b/packages/babel-plugin-jsx/test/__snapshots__/snapshot.test.ts.snap @@ -63,6 +63,12 @@ _createVNode(_Fragment, null, [_withDirectives(_createVNode(_resolveComponent("A }]])]);" `; +exports[`directive in outer scope > directive in outer scope 1`] = ` +"import { resolveComponent as _resolveComponent, createVNode as _createVNode, withDirectives as _withDirectives } from "vue"; +const vXxx = {}; +() => _withDirectives(_createVNode(_resolveComponent("A"), null, null, 512), [[vXxx]]);" +`; + exports[`directive in scope > directive in scope 1`] = ` "import { resolveComponent as _resolveComponent, createVNode as _createVNode, withDirectives as _withDirectives } from "vue"; const vXxx = {}; diff --git a/packages/babel-plugin-jsx/test/snapshot.test.ts b/packages/babel-plugin-jsx/test/snapshot.test.ts index 00e92c9c..c3b80529 100644 --- a/packages/babel-plugin-jsx/test/snapshot.test.ts +++ b/packages/babel-plugin-jsx/test/snapshot.test.ts @@ -162,6 +162,15 @@ const transpile = (source: string, options: VueJSXPluginOptions = {}) => `, }, + { + name: 'directive in outer scope', + from: ` + const vXxx = {}; + () => ( + + ); + `, + }, { name: 'vModels', from: '',