wip: support resolving directives from setup scope variables by naming convention
v-my-dir can be resovled from setup scope variable named "vMyDir".
This commit is contained in:
parent
2f32e4a077
commit
ae2caad740
@ -29,8 +29,7 @@ import {
|
||||
isObject,
|
||||
isReservedProp,
|
||||
capitalize,
|
||||
camelize,
|
||||
EMPTY_OBJ
|
||||
camelize
|
||||
} from '@vue/shared'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import {
|
||||
@ -255,34 +254,16 @@ export function resolveComponentType(
|
||||
}
|
||||
|
||||
// 3. user component (from setup bindings)
|
||||
const bindings = context.bindingMetadata
|
||||
if (bindings !== EMPTY_OBJ) {
|
||||
const checkType = (type: BindingTypes) => {
|
||||
let resolvedTag = tag
|
||||
if (
|
||||
bindings[resolvedTag] === type ||
|
||||
bindings[(resolvedTag = camelize(tag))] === type ||
|
||||
bindings[(resolvedTag = capitalize(camelize(tag)))] === type
|
||||
) {
|
||||
return resolvedTag
|
||||
}
|
||||
}
|
||||
const tagFromConst = checkType(BindingTypes.SETUP_CONST)
|
||||
if (tagFromConst) {
|
||||
return context.inline
|
||||
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
|
||||
tagFromConst
|
||||
: `$setup[${JSON.stringify(tagFromConst)}]`
|
||||
}
|
||||
const tagFromSetup =
|
||||
checkType(BindingTypes.SETUP_LET) ||
|
||||
checkType(BindingTypes.SETUP_REF) ||
|
||||
checkType(BindingTypes.SETUP_MAYBE_REF)
|
||||
if (tagFromSetup) {
|
||||
return context.inline
|
||||
? // setup scope bindings that may be refs need to be unrefed
|
||||
`${context.helperString(UNREF)}(${tagFromSetup})`
|
||||
: `$setup[${JSON.stringify(tagFromSetup)}]`
|
||||
// this is skipped in browser build since browser builds do not perform
|
||||
// binding analysis.
|
||||
if (!__BROWSER__) {
|
||||
const fromSetup = resolveSetupReference(
|
||||
tag,
|
||||
capitalize(camelize(tag)),
|
||||
context
|
||||
)
|
||||
if (fromSetup) {
|
||||
return fromSetup
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +273,45 @@ export function resolveComponentType(
|
||||
return toValidAssetId(tag, `component`)
|
||||
}
|
||||
|
||||
function resolveSetupReference(
|
||||
name: string,
|
||||
interopName: string,
|
||||
context: TransformContext
|
||||
) {
|
||||
const bindings = context.bindingMetadata
|
||||
if (!bindings) {
|
||||
return
|
||||
}
|
||||
|
||||
const checkType = (type: BindingTypes) => {
|
||||
if (bindings[name] === type) {
|
||||
return name
|
||||
}
|
||||
if (bindings[interopName] === type) {
|
||||
return interopName
|
||||
}
|
||||
}
|
||||
|
||||
const fromConst = checkType(BindingTypes.SETUP_CONST)
|
||||
if (fromConst) {
|
||||
return context.inline
|
||||
? // in inline mode, const setup bindings (e.g. imports) can be used as-is
|
||||
fromConst
|
||||
: `$setup[${JSON.stringify(fromConst)}]`
|
||||
}
|
||||
|
||||
const fromMaybeRef =
|
||||
checkType(BindingTypes.SETUP_LET) ||
|
||||
checkType(BindingTypes.SETUP_REF) ||
|
||||
checkType(BindingTypes.SETUP_MAYBE_REF)
|
||||
if (fromMaybeRef) {
|
||||
return context.inline
|
||||
? // setup scope bindings that may be refs need to be unrefed
|
||||
`${context.helperString(UNREF)}(${fromMaybeRef})`
|
||||
: `$setup[${JSON.stringify(fromMaybeRef)}]`
|
||||
}
|
||||
}
|
||||
|
||||
export type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
|
||||
|
||||
export function buildProps(
|
||||
@ -590,12 +610,28 @@ function buildDirectiveArgs(
|
||||
const dirArgs: ArrayExpression['elements'] = []
|
||||
const runtime = directiveImportMap.get(dir)
|
||||
if (runtime) {
|
||||
// built-in directive with runtime
|
||||
dirArgs.push(context.helperString(runtime))
|
||||
} else {
|
||||
// inject statement for resolving directive
|
||||
context.helper(RESOLVE_DIRECTIVE)
|
||||
context.directives.add(dir.name)
|
||||
dirArgs.push(toValidAssetId(dir.name, `directive`))
|
||||
// user directive.
|
||||
// see if we have directives exposed via <script setup>
|
||||
const fromSetup =
|
||||
!__BROWSER__ &&
|
||||
resolveSetupReference(
|
||||
dir.name,
|
||||
// v-my-dir -> vMyDir
|
||||
'v' + capitalize(camelize(dir.name)),
|
||||
context
|
||||
)
|
||||
|
||||
if (fromSetup) {
|
||||
dirArgs.push(fromSetup)
|
||||
} else {
|
||||
// inject statement for resolving directive
|
||||
context.helper(RESOLVE_DIRECTIVE)
|
||||
context.directives.add(dir.name)
|
||||
dirArgs.push(toValidAssetId(dir.name, `directive`))
|
||||
}
|
||||
}
|
||||
const { loc } = dir
|
||||
if (dir.exp) dirArgs.push(dir.exp)
|
||||
|
@ -177,6 +177,32 @@ return (_ctx, _cache) => {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode referencing scope components and directives 1`] = `
|
||||
"import { unref as _unref, createVNode as _createVNode, withDirectives as _withDirectives, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
import ChildComp from './Child.vue'
|
||||
import SomeOtherComp from './Other.vue'
|
||||
import vMyDir from './my-dir'
|
||||
|
||||
export default {
|
||||
expose: [],
|
||||
setup(__props) {
|
||||
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||
_withDirectives(_createVNode(\\"div\\", null, null, 512 /* NEED_PATCH */), [
|
||||
[_unref(vMyDir)]
|
||||
]),
|
||||
_createVNode(ChildComp),
|
||||
_createVNode(SomeOtherComp)
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode should work 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
|
@ -147,6 +147,29 @@ const bar = 1
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('referencing scope components and directives', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
<script setup>
|
||||
import ChildComp from './Child.vue'
|
||||
import SomeOtherComp from './Other.vue'
|
||||
import vMyDir from './my-dir'
|
||||
</script>
|
||||
<template>
|
||||
<div v-my-dir></div>
|
||||
<ChildComp/>
|
||||
<some-other-comp/>
|
||||
</template>
|
||||
`,
|
||||
{ inlineTemplate: true }
|
||||
)
|
||||
expect(content).toMatch('[_unref(vMyDir)]')
|
||||
expect(content).toMatch('_createVNode(ChildComp)')
|
||||
// kebab-case component support
|
||||
expect(content).toMatch('_createVNode(SomeOtherComp)')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('avoid unref() when necessary', () => {
|
||||
// function, const, component import
|
||||
const { content } = compile(
|
||||
|
@ -19,7 +19,8 @@ export const compilerOptions: CompilerOptions = reactive({
|
||||
setupConst: BindingTypes.SETUP_CONST,
|
||||
setupLet: BindingTypes.SETUP_LET,
|
||||
setupMaybeRef: BindingTypes.SETUP_MAYBE_REF,
|
||||
setupProp: BindingTypes.PROPS
|
||||
setupProp: BindingTypes.PROPS,
|
||||
vMySetupDir: BindingTypes.SETUP_CONST
|
||||
}
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user