isObject,
isReservedProp,
capitalize,
- camelize,
- EMPTY_OBJ
+ camelize
} from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import {
}
// 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
}
}
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(
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)
}"
`;
+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\\"
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(